summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile3
-rw-r--r--drivers/acpi/Kconfig13
-rw-r--r--drivers/acpi/ac.c14
-rw-r--r--drivers/acpi/acpica/Makefile5
-rw-r--r--drivers/acpi/acpica/acdebug.h2
-rw-r--r--drivers/acpi/acpica/acevents.h5
-rw-r--r--drivers/acpi/acpica/acglobal.h9
-rw-r--r--drivers/acpi/acpica/achware.h7
-rw-r--r--drivers/acpi/acpica/aclocal.h15
-rw-r--r--drivers/acpi/acpica/acmacros.h4
-rw-r--r--drivers/acpi/acpica/acnamesp.h12
-rw-r--r--drivers/acpi/acpica/acobject.h2
-rw-r--r--drivers/acpi/acpica/acutils.h56
-rw-r--r--drivers/acpi/acpica/dsmethod.c2
-rw-r--r--drivers/acpi/acpica/dswexec.c19
-rw-r--r--drivers/acpi/acpica/evevent.c41
-rw-r--r--drivers/acpi/acpica/evgpeblk.c47
-rw-r--r--drivers/acpi/acpica/evgpeinit.c31
-rw-r--r--drivers/acpi/acpica/evmisc.c2
-rw-r--r--drivers/acpi/acpica/evrgnini.c14
-rw-r--r--drivers/acpi/acpica/evxface.c19
-rw-r--r--drivers/acpi/acpica/evxfevnt.c61
-rw-r--r--drivers/acpi/acpica/evxfregn.c6
-rw-r--r--drivers/acpi/acpica/exfldio.c75
-rw-r--r--drivers/acpi/acpica/exmutex.c10
-rw-r--r--drivers/acpi/acpica/exprep.c45
-rw-r--r--drivers/acpi/acpica/exregion.c4
-rw-r--r--drivers/acpi/acpica/hwpci.c412
-rw-r--r--drivers/acpi/acpica/nsrepair2.c163
-rw-r--r--drivers/acpi/acpica/nsutils.c98
-rw-r--r--drivers/acpi/acpica/tbfadt.c4
-rw-r--r--drivers/acpi/acpica/utdebug.c7
-rw-r--r--drivers/acpi/acpica/uteval.c147
-rw-r--r--drivers/acpi/acpica/utglobal.c9
-rw-r--r--drivers/acpi/acpica/utids.c45
-rw-r--r--drivers/acpi/acpica/utinit.c4
-rw-r--r--drivers/acpi/acpica/utmath.c23
-rw-r--r--drivers/acpi/acpica/utmisc.c162
-rw-r--r--drivers/acpi/acpica/utmutex.c37
-rw-r--r--drivers/acpi/acpica/utosi.c380
-rw-r--r--drivers/acpi/acpica/utxface.c138
-rw-r--r--drivers/acpi/acpica/utxferror.c415
-rw-r--r--drivers/acpi/battery.c94
-rw-r--r--drivers/acpi/bus.c7
-rw-r--r--drivers/acpi/button.c4
-rw-r--r--drivers/acpi/dock.c7
-rw-r--r--drivers/acpi/ec.c9
-rw-r--r--drivers/acpi/fan.c139
-rw-r--r--drivers/acpi/osl.c463
-rw-r--r--drivers/acpi/pci_irq.c1
-rw-r--r--drivers/acpi/pci_link.c1
-rw-r--r--drivers/acpi/pci_root.c1
-rw-r--r--drivers/acpi/power.c167
-rw-r--r--drivers/acpi/processor_driver.c22
-rw-r--r--drivers/acpi/processor_idle.c2
-rw-r--r--drivers/acpi/processor_thermal.c178
-rw-r--r--drivers/acpi/processor_throttling.c4
-rw-r--r--drivers/acpi/sbs.c25
-rw-r--r--drivers/acpi/scan.c46
-rw-r--r--drivers/acpi/sleep.c28
-rw-r--r--drivers/acpi/sleep.h1
-rw-r--r--drivers/acpi/thermal.c436
-rw-r--r--drivers/acpi/video.c771
-rw-r--r--drivers/base/node.c14
-rw-r--r--drivers/base/platform.c1
-rw-r--r--drivers/base/power/runtime.c2
-rw-r--r--drivers/block/loop.c4
-rw-r--r--drivers/block/xsysace.c3
-rw-r--r--drivers/char/agp/Makefile1
-rw-r--r--drivers/char/agp/agp.h5
-rw-r--r--drivers/char/agp/amd-k7-agp.c6
-rw-r--r--drivers/char/agp/backend.c22
-rw-r--r--drivers/char/agp/generic.c8
-rw-r--r--drivers/char/agp/intel-agp.c201
-rw-r--r--drivers/char/agp/intel-agp.h43
-rw-r--r--drivers/char/agp/intel-gtt.c1612
-rw-r--r--drivers/char/hpet.c40
-rw-r--r--drivers/char/hvc_iucv.c4
-rw-r--r--drivers/char/hvc_tile.c5
-rw-r--r--drivers/char/hvc_xen.c3
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c30
-rw-r--r--drivers/char/keyboard.c31
-rw-r--r--drivers/char/mem.c4
-rw-r--r--drivers/char/sysrq.c15
-rw-r--r--drivers/clocksource/sh_cmt.c12
-rw-r--r--drivers/cpufreq/cpufreq.c4
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c42
-rw-r--r--drivers/crypto/hifn_795x.c4
-rw-r--r--drivers/edac/edac_core.h11
-rw-r--r--drivers/edac/edac_mc.c12
-rw-r--r--drivers/edac/edac_mc_sysfs.c82
-rw-r--r--drivers/edac/i7core_edac.c432
-rw-r--r--drivers/firewire/Kconfig5
-rw-r--r--drivers/firewire/Makefile1
-rw-r--r--drivers/firewire/init_ohci1394_dma.c (renamed from drivers/ieee1394/init_ohci1394_dma.c)76
-rw-r--r--drivers/gpio/pca953x.c13
-rw-r--r--drivers/gpio/xilinx_gpio.c6
-rw-r--r--drivers/gpu/Makefile2
-rw-r--r--drivers/gpu/drm/Makefile2
-rw-r--r--drivers/gpu/drm/drm_agpsupport.c40
-rw-r--r--drivers/gpu/drm/drm_context.c8
-rw-r--r--drivers/gpu/drm/drm_crtc.c3
-rw-r--r--drivers/gpu/drm/drm_debugfs.c1
-rw-r--r--drivers/gpu/drm/drm_drawable.c198
-rw-r--r--drivers/gpu/drm/drm_drv.c10
-rw-r--r--drivers/gpu/drm/drm_edid.c93
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c32
-rw-r--r--drivers/gpu/drm/drm_gem.c14
-rw-r--r--drivers/gpu/drm/drm_info.c14
-rw-r--r--drivers/gpu/drm/drm_lock.c30
-rw-r--r--drivers/gpu/drm/drm_memory.c14
-rw-r--r--drivers/gpu/drm/drm_proc.c14
-rw-r--r--drivers/gpu/drm/drm_scatter.c2
-rw-r--r--drivers/gpu/drm/drm_stub.c4
-rw-r--r--drivers/gpu/drm/drm_vm.c13
-rw-r--r--drivers/gpu/drm/i810/i810_drv.c2
-rw-r--r--drivers/gpu/drm/i830/i830_drv.c2
-rw-r--r--drivers/gpu/drm/i915/Makefile4
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7017.c66
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7xxx.c10
-rw-r--r--drivers/gpu/drm/i915/dvo_ivch.c10
-rw-r--r--drivers/gpu/drm/i915/dvo_sil164.c10
-rw-r--r--drivers/gpu/drm/i915/dvo_tfp410.c10
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c336
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c360
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c214
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h271
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c2230
-rw-r--r--drivers/gpu/drm/i915/i915_gem_debug.c148
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c72
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c54
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c264
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h335
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c28
-rw-r--r--drivers/gpu/drm/i915/intel_acpi.c286
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c234
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h6
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c127
-rw-r--r--drivers/gpu/drm/i915/intel_display.c2357
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c658
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h160
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c69
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c29
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c193
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c484
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c435
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c16
-rw-r--r--drivers/gpu/drm/i915/intel_opregion.c (renamed from drivers/gpu/drm/i915/i915_opregion.c)181
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c1001
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c109
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c457
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h81
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c1076
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c165
-rw-r--r--drivers/gpu/drm/mga/mga_drv.c2
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig1
-rw-r--r--drivers/gpu/drm/nouveau/Makefile6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c374
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h43
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c290
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_calc.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_channel.c23
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c54
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_debugfs.c16
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c32
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.c23
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h253
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c318
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_grctx.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hw.c45
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.h5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_irq.c123
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c363
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_notifier.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_object.c776
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_perf.c205
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.c518
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.h74
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ramht.c289
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ramht.h55
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_reg.h9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sgdma.c68
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c123
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_temp.c309
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_volt.c212
-rw-r--r--drivers/gpu/drm/nouveau/nv04_crtc.c60
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dac.c11
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dfp.c39
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fbcon.c9
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fifo.c68
-rw-r--r--drivers/gpu/drm/nouveau/nv04_instmem.c140
-rw-r--r--drivers/gpu/drm/nouveau/nv04_pm.c81
-rw-r--r--drivers/gpu/drm/nouveau/nv04_tv.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv10_fifo.c19
-rw-r--r--drivers/gpu/drm/nouveau/nv10_graph.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv17_tv.c110
-rw-r--r--drivers/gpu/drm/nouveau/nv17_tv.h15
-rw-r--r--drivers/gpu/drm/nouveau/nv17_tv_modes.c48
-rw-r--r--drivers/gpu/drm/nouveau/nv20_graph.c506
-rw-r--r--drivers/gpu/drm/nouveau/nv40_fifo.c20
-rw-r--r--drivers/gpu/drm/nouveau/nv40_graph.c16
-rw-r--r--drivers/gpu/drm/nouveau/nv40_grctx.c6
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c87
-rw-r--r--drivers/gpu/drm/nouveau/nv50_cursor.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_dac.c4
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c92
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fb.c40
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fbcon.c4
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fifo.c286
-rw-r--r--drivers/gpu/drm/nouveau/nv50_graph.c51
-rw-r--r--drivers/gpu/drm/nouveau/nv50_grctx.c3305
-rw-r--r--drivers/gpu/drm/nouveau/nv50_instmem.c418
-rw-r--r--drivers/gpu/drm/nouveau/nv50_pm.c131
-rw-r--r--drivers/gpu/drm/nouveau/nv50_sor.c4
-rw-r--r--drivers/gpu/drm/nouveau/nva3_pm.c95
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_instmem.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvreg.h1
-rw-r--r--drivers/gpu/drm/r128/r128_drv.c2
-rw-r--r--drivers/gpu/drm/radeon/Makefile2
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c406
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c564
-rw-r--r--drivers/gpu/drm/radeon/evergreen_blit_kms.c772
-rw-r--r--drivers/gpu/drm/radeon/evergreen_blit_shaders.c348
-rw-r--r--drivers/gpu/drm/radeon/evergreen_blit_shaders.h35
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h20
-rw-r--r--drivers/gpu/drm/radeon/r100.c100
-rw-r--r--drivers/gpu/drm/radeon/r300.c15
-rw-r--r--drivers/gpu/drm/radeon/r420.c16
-rw-r--r--drivers/gpu/drm/radeon/r520.c11
-rw-r--r--drivers/gpu/drm/radeon/r600.c179
-rw-r--r--drivers/gpu/drm/radeon/r600_blit_kms.c51
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c36
-rw-r--r--drivers/gpu/drm/radeon/r600d.h21
-rw-r--r--drivers/gpu/drm/radeon/radeon.h23
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h11
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c129
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c65
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c15
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c83
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c405
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c11
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c16
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c10
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c49
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h53
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c70
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c34
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/evergreen8
-rw-r--r--drivers/gpu/drm/radeon/rs400.c15
-rw-r--r--drivers/gpu/drm/radeon/rs600.c15
-rw-r--r--drivers/gpu/drm/radeon/rs690.c15
-rw-r--r--drivers/gpu/drm/radeon/rv515.c15
-rw-r--r--drivers/gpu/drm/radeon/rv770.c38
-rw-r--r--drivers/gpu/drm/savage/savage_drv.c2
-rw-r--r--drivers/gpu/drm/sis/sis_drv.c3
-rw-r--r--drivers/gpu/drm/tdfx/tdfx_drv.c2
-rw-r--r--drivers/gpu/drm/ttm/Makefile3
-rw-r--r--drivers/gpu/drm/ttm/ttm_agp_backend.c3
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c305
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_manager.c148
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c20
-rw-r--r--drivers/gpu/drm/via/via_drv.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/Makefile2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c84
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c130
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h40
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c29
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c13
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c38
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c137
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c3
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c200
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c28
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c75
-rw-r--r--drivers/gpu/stub/Kconfig13
-rw-r--r--drivers/gpu/stub/Makefile1
-rw-r--r--drivers/gpu/stub/poulsbo.c64
-rw-r--r--drivers/hid/hid-core.c5
-rw-r--r--drivers/hid/hid-ids.h4
-rw-r--r--drivers/hid/hid-input.c108
-rw-r--r--drivers/hwmon/Kconfig40
-rw-r--r--drivers/hwmon/Makefile3
-rw-r--r--drivers/hwmon/coretemp.c28
-rw-r--r--drivers/hwmon/gpio-fan.c558
-rw-r--r--drivers/hwmon/hp_accel.c32
-rw-r--r--drivers/hwmon/lis3lv02d.c364
-rw-r--r--drivers/hwmon/lis3lv02d.h47
-rw-r--r--drivers/hwmon/lis3lv02d_i2c.c132
-rw-r--r--drivers/hwmon/lis3lv02d_spi.c5
-rw-r--r--drivers/hwmon/ltc4261.c315
-rw-r--r--drivers/hwmon/pkgtemp.c32
-rw-r--r--drivers/hwmon/via-cputemp.c11
-rw-r--r--drivers/i2c/busses/i2c-sh7760.c4
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c23
-rw-r--r--drivers/idle/intel_idle.c61
-rw-r--r--drivers/ieee1394/Kconfig182
-rw-r--r--drivers/ieee1394/Makefile18
-rw-r--r--drivers/ieee1394/config_roms.c194
-rw-r--r--drivers/ieee1394/config_roms.h19
-rw-r--r--drivers/ieee1394/csr.c843
-rw-r--r--drivers/ieee1394/csr.h99
-rw-r--r--drivers/ieee1394/csr1212.c1467
-rw-r--r--drivers/ieee1394/csr1212.h383
-rw-r--r--drivers/ieee1394/dma.c289
-rw-r--r--drivers/ieee1394/dma.h89
-rw-r--r--drivers/ieee1394/dv1394-private.h587
-rw-r--r--drivers/ieee1394/dv1394.c2584
-rw-r--r--drivers/ieee1394/dv1394.h305
-rw-r--r--drivers/ieee1394/eth1394.c1720
-rw-r--r--drivers/ieee1394/eth1394.h234
-rw-r--r--drivers/ieee1394/highlevel.c691
-rw-r--r--drivers/ieee1394/highlevel.h141
-rw-r--r--drivers/ieee1394/hosts.c249
-rw-r--r--drivers/ieee1394/hosts.h201
-rw-r--r--drivers/ieee1394/ieee1394-ioctl.h106
-rw-r--r--drivers/ieee1394/ieee1394.h220
-rw-r--r--drivers/ieee1394/ieee1394_core.c1380
-rw-r--r--drivers/ieee1394/ieee1394_core.h172
-rw-r--r--drivers/ieee1394/ieee1394_hotplug.h19
-rw-r--r--drivers/ieee1394/ieee1394_transactions.c595
-rw-r--r--drivers/ieee1394/ieee1394_transactions.h40
-rw-r--r--drivers/ieee1394/ieee1394_types.h69
-rw-r--r--drivers/ieee1394/iso.c568
-rw-r--r--drivers/ieee1394/iso.h195
-rw-r--r--drivers/ieee1394/nodemgr.c1901
-rw-r--r--drivers/ieee1394/nodemgr.h186
-rw-r--r--drivers/ieee1394/ohci1394.c3590
-rw-r--r--drivers/ieee1394/ohci1394.h453
-rw-r--r--drivers/ieee1394/pcilynx.c1554
-rw-r--r--drivers/ieee1394/pcilynx.h468
-rw-r--r--drivers/ieee1394/raw1394-private.h81
-rw-r--r--drivers/ieee1394/raw1394.c3096
-rw-r--r--drivers/ieee1394/raw1394.h191
-rw-r--r--drivers/ieee1394/sbp2.c2138
-rw-r--r--drivers/ieee1394/sbp2.h346
-rw-r--r--drivers/ieee1394/video1394.c1528
-rw-r--r--drivers/ieee1394/video1394.h67
-rw-r--r--drivers/infiniband/core/agent.c29
-rw-r--r--drivers/infiniband/core/cma.c313
-rw-r--r--drivers/infiniband/core/iwcm.c4
-rw-r--r--drivers/infiniband/core/mad.c27
-rw-r--r--drivers/infiniband/core/multicast.c23
-rw-r--r--drivers/infiniband/core/sa_query.c30
-rw-r--r--drivers/infiniband/core/sysfs.c15
-rw-r--r--drivers/infiniband/core/ucma.c92
-rw-r--r--drivers/infiniband/core/ud_header.c140
-rw-r--r--drivers/infiniband/core/user_mad.c2
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c2
-rw-r--r--drivers/infiniband/core/verbs.c16
-rw-r--r--drivers/infiniband/hw/amso1100/Kbuild4
-rw-r--r--drivers/infiniband/hw/amso1100/c2_intr.c4
-rw-r--r--drivers/infiniband/hw/cxgb3/Makefile6
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_hal.c1
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_wr.h16
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c4
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_ev.c17
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c24
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_qp.c25
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_user.h8
-rw-r--r--drivers/infiniband/hw/cxgb4/Makefile2
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c178
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c28
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c191
-rw-r--r--drivers/infiniband/hw/cxgb4/ev.c2
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h68
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c11
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c44
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c283
-rw-r--r--drivers/infiniband/hw/cxgb4/resource.c62
-rw-r--r--drivers/infiniband/hw/cxgb4/t4.h44
-rw-r--r--drivers/infiniband/hw/cxgb4/user.h7
-rw-r--r--drivers/infiniband/hw/ehca/ehca_mrmw.c6
-rw-r--r--drivers/infiniband/hw/ipath/Makefile2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_fs.c1
-rw-r--r--drivers/infiniband/hw/mlx4/ah.c163
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c32
-rw-r--r--drivers/infiniband/hw/mlx4/main.c553
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h32
-rw-r--r--drivers/infiniband/hw/mlx4/mr.c2
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c195
-rw-r--r--drivers/infiniband/hw/mthca/mthca_qp.c2
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c3
-rw-r--r--drivers/infiniband/hw/nes/nes_nic.c1
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c16
-rw-r--r--drivers/infiniband/hw/qib/qib.h2
-rw-r--r--drivers/infiniband/hw/qib/qib_fs.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_init.c1
-rw-r--r--drivers/infiniband/hw/qib/qib_pcie.c8
-rw-r--r--drivers/infiniband/hw/qib/qib_rc.c5
-rw-r--r--drivers/infiniband/hw/qib/qib_uc.c6
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c14
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c6
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c236
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h21
-rw-r--r--drivers/input/evdev.c100
-rw-r--r--drivers/input/gameport/emu10k1-gp.c44
-rw-r--r--drivers/input/gameport/fm801-gp.c10
-rw-r--r--drivers/input/input.c198
-rw-r--r--drivers/input/keyboard/Kconfig28
-rw-r--r--drivers/input/keyboard/Makefile3
-rw-r--r--drivers/input/keyboard/adp5588-keys.c2
-rw-r--r--drivers/input/keyboard/hil_kbd.c2
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c408
-rw-r--r--drivers/input/keyboard/omap4-keypad.c318
-rw-r--r--drivers/input/keyboard/tnetv107x-keypad.c340
-rw-r--r--drivers/input/keyboard/twl4030_keypad.c7
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ab8500-ponkey.c157
-rw-r--r--drivers/input/misc/ati_remote2.c93
-rw-r--r--drivers/input/misc/powermate.c2
-rw-r--r--drivers/input/misc/twl4030-vibra.c4
-rw-r--r--drivers/input/mouse/elantech.c2
-rw-r--r--drivers/input/mouse/psmouse-base.c4
-rw-r--r--drivers/input/mouse/synaptics.c38
-rw-r--r--drivers/input/mouse/synaptics.h2
-rw-r--r--drivers/input/mouse/trackpoint.c2
-rw-r--r--drivers/input/mousedev.c2
-rw-r--r--drivers/input/serio/Kconfig9
-rw-r--r--drivers/input/serio/Makefile1
-rw-r--r--drivers/input/serio/i8042.c2
-rw-r--r--drivers/input/serio/ps2mult.c318
-rw-r--r--drivers/input/serio/serio.c125
-rw-r--r--drivers/input/sparse-keymap.c81
-rw-r--r--drivers/input/tablet/Kconfig11
-rw-r--r--drivers/input/tablet/Makefile1
-rw-r--r--drivers/input/tablet/hanwang.c446
-rw-r--r--drivers/input/tablet/wacom.h1
-rw-r--r--drivers/input/tablet/wacom_sys.c77
-rw-r--r--drivers/input/tablet/wacom_wac.c253
-rw-r--r--drivers/input/tablet/wacom_wac.h15
-rw-r--r--drivers/input/touchscreen/Kconfig43
-rw-r--r--drivers/input/touchscreen/Makefile4
-rw-r--r--drivers/input/touchscreen/ad7877.c144
-rw-r--r--drivers/input/touchscreen/ads7846.c886
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c648
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c4
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c6
-rw-r--r--drivers/input/touchscreen/intel-mid-touch.c687
-rw-r--r--drivers/input/touchscreen/lpc32xx_ts.c411
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c2
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c11
-rw-r--r--drivers/input/touchscreen/tnetv107x-ts.c396
-rw-r--r--drivers/input/touchscreen/tps6507x-ts.c3
-rw-r--r--drivers/input/touchscreen/tsc2007.c2
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c185
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c18
-rw-r--r--drivers/macintosh/windfarm_pm121.c2
-rw-r--r--drivers/md/dm-snap-persistent.c2
-rw-r--r--drivers/media/IR/ir-keytable.c393
-rw-r--r--drivers/mfd/ab8500-core.c20
-rw-r--r--drivers/mfd/sh_mobile_sdhi.c2
-rw-r--r--drivers/mfd/twl-core.c6
-rw-r--r--drivers/mfd/twl4030-codec.c8
-rw-r--r--drivers/misc/Kconfig45
-rw-r--r--drivers/misc/Makefile4
-rw-r--r--drivers/misc/ad525x_dpot-i2c.c2
-rw-r--r--drivers/misc/ad525x_dpot-spi.c6
-rw-r--r--drivers/misc/ad525x_dpot.c117
-rw-r--r--drivers/misc/ad525x_dpot.h37
-rw-r--r--drivers/misc/apds9802als.c347
-rw-r--r--drivers/misc/apds990x.c1295
-rw-r--r--drivers/misc/bh1770glc.c1413
-rw-r--r--drivers/misc/ibmasm/ibmasmfs.c1
-rw-r--r--drivers/misc/isl29020.c248
-rw-r--r--drivers/misc/lkdtm.c128
-rw-r--r--drivers/misc/phantom.c8
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c17
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/mmc/host/omap_hsmmc.c43
-rw-r--r--drivers/mtd/devices/m25p80.c7
-rw-r--r--drivers/mtd/maps/physmap_of.c2
-rw-r--r--drivers/mtd/nand/davinci_nand.c61
-rw-r--r--drivers/mtd/ofpart.c4
-rw-r--r--drivers/net/Kconfig29
-rw-r--r--drivers/net/Makefile2
-rw-r--r--drivers/net/bfin_mac.c145
-rw-r--r--drivers/net/bfin_mac.h2
-rw-r--r--drivers/net/davinci_cpdma.c965
-rw-r--r--drivers/net/davinci_cpdma.h108
-rw-r--r--drivers/net/davinci_emac.c1338
-rw-r--r--drivers/net/davinci_mdio.c475
-rw-r--r--drivers/net/mlx4/en_main.c15
-rw-r--r--drivers/net/mlx4/en_netdev.c10
-rw-r--r--drivers/net/mlx4/en_port.c4
-rw-r--r--drivers/net/mlx4/en_port.h3
-rw-r--r--drivers/net/mlx4/fw.c3
-rw-r--r--drivers/net/mlx4/intf.c21
-rw-r--r--drivers/net/mlx4/main.c4
-rw-r--r--drivers/net/mlx4/mlx4_en.h1
-rw-r--r--drivers/net/mlx4/port.c19
-rw-r--r--drivers/net/xilinx_emaclite.c8
-rw-r--r--drivers/of/Kconfig5
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/address.c2
-rw-r--r--drivers/of/base.c4
-rw-r--r--drivers/of/device.c27
-rw-r--r--drivers/of/fdt.c2
-rw-r--r--drivers/of/irq.c39
-rw-r--r--drivers/of/of_i2c.c1
-rw-r--r--drivers/of/pdt.c276
-rw-r--r--drivers/of/platform.c34
-rw-r--r--drivers/oprofile/oprofilefs.c1
-rw-r--r--drivers/platform/x86/Kconfig53
-rw-r--r--drivers/platform/x86/Makefile6
-rw-r--r--drivers/platform/x86/acer-wmi.c2
-rw-r--r--drivers/platform/x86/asus-laptop.c171
-rw-r--r--drivers/platform/x86/dell-laptop.c77
-rw-r--r--drivers/platform/x86/dell-wmi.c256
-rw-r--r--drivers/platform/x86/eeepc-laptop.c16
-rw-r--r--drivers/platform/x86/eeepc-wmi.c56
-rw-r--r--drivers/platform/x86/hdaps.c (renamed from drivers/hwmon/hdaps.c)2
-rw-r--r--drivers/platform/x86/hp-wmi.c172
-rw-r--r--drivers/platform/x86/ibm_rtl.c341
-rw-r--r--drivers/platform/x86/ideapad-laptop.c (renamed from drivers/platform/x86/ideapad_acpi.c)238
-rw-r--r--drivers/platform/x86/intel_pmic_gpio.c26
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c1
-rw-r--r--drivers/platform/x86/panasonic-laptop.c194
-rw-r--r--drivers/platform/x86/topstar-laptop.c161
-rw-r--r--drivers/platform/x86/toshiba_acpi.c191
-rw-r--r--drivers/platform/x86/wmi.c308
-rw-r--r--drivers/platform/x86/xo1-rfkill.c85
-rw-r--r--drivers/pnp/base.h5
-rw-r--r--drivers/pnp/core.c8
-rw-r--r--drivers/pnp/driver.c2
-rw-r--r--drivers/pnp/pnpacpi/core.c31
-rw-r--r--drivers/pnp/resource.c10
-rw-r--r--drivers/power/Kconfig23
-rw-r--r--drivers/power/Makefile19
-rw-r--r--drivers/power/bq20z75.c493
-rw-r--r--drivers/power/bq27x00_battery.c1
-rw-r--r--drivers/power/ds2760_battery.c1
-rw-r--r--drivers/power/ds2782_battery.c12
-rw-r--r--drivers/power/isp1704_charger.c369
-rw-r--r--drivers/power/jz4740-battery.c1
-rw-r--r--drivers/power/olpc_battery.c8
-rw-r--r--drivers/power/pcf50633-charger.c1
-rw-r--r--drivers/power/power_supply_sysfs.c4
-rw-r--r--drivers/power/twl4030_charger.c565
-rw-r--r--drivers/power/wm831x_power.c2
-rw-r--r--drivers/rtc/Kconfig12
-rw-r--r--drivers/s390/block/dasd.c24
-rw-r--r--drivers/s390/block/dasd_3990_erp.c3
-rw-r--r--drivers/s390/block/dasd_diag.c19
-rw-r--r--drivers/s390/block/dasd_diag.h4
-rw-r--r--drivers/s390/block/dasd_eckd.c68
-rw-r--r--drivers/s390/block/dasd_proc.c1
-rw-r--r--drivers/s390/char/sclp.c14
-rw-r--r--drivers/s390/char/vmlogrdr.c4
-rw-r--r--drivers/s390/cio/blacklist.c10
-rw-r--r--drivers/s390/cio/chp.c41
-rw-r--r--drivers/s390/cio/chp.h12
-rw-r--r--drivers/s390/cio/chsc.c291
-rw-r--r--drivers/s390/cio/chsc.h28
-rw-r--r--drivers/s390/cio/chsc_sch.c12
-rw-r--r--drivers/s390/cio/css.c50
-rw-r--r--drivers/s390/cio/device.c18
-rw-r--r--drivers/s390/cio/device_fsm.c41
-rw-r--r--drivers/s390/cio/device_pgid.c23
-rw-r--r--drivers/s390/cio/io_sch.h7
-rw-r--r--drivers/s390/crypto/ap_bus.c9
-rw-r--r--drivers/s390/kvm/kvm_virtio.c9
-rw-r--r--drivers/sbus/char/jsflash.c2
-rw-r--r--drivers/serial/68328serial.h5
-rw-r--r--drivers/serial/Kconfig27
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/of_serial.c12
-rw-r--r--drivers/serial/omap-serial.c1333
-rw-r--r--drivers/serial/sh-sci.h17
-rw-r--r--drivers/sh/Kconfig25
-rw-r--r--drivers/sh/Makefile7
-rw-r--r--drivers/sh/clk/Makefile3
-rw-r--r--drivers/sh/clk/core.c (renamed from drivers/sh/clk.c)203
-rw-r--r--drivers/sh/clk/cpg.c (renamed from drivers/sh/clk-cpg.c)11
-rw-r--r--drivers/sh/intc.c1390
-rw-r--r--drivers/sh/intc/Kconfig35
-rw-r--r--drivers/sh/intc/Makefile5
-rw-r--r--drivers/sh/intc/access.c237
-rw-r--r--drivers/sh/intc/balancing.c97
-rw-r--r--drivers/sh/intc/chip.c215
-rw-r--r--drivers/sh/intc/core.c469
-rw-r--r--drivers/sh/intc/dynamic.c135
-rw-r--r--drivers/sh/intc/handle.c307
-rw-r--r--drivers/sh/intc/internals.h186
-rw-r--r--drivers/sh/intc/userimask.c83
-rw-r--r--drivers/sh/intc/virq-debugfs.c64
-rw-r--r--drivers/sh/intc/virq.c255
-rw-r--r--drivers/sh/pfc.c17
-rw-r--r--drivers/spi/Kconfig7
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi_tegra.c618
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/mrst-touchscreen/Kconfig7
-rw-r--r--drivers/staging/mrst-touchscreen/Makefile3
-rw-r--r--drivers/staging/mrst-touchscreen/TODO2
-rw-r--r--drivers/staging/mrst-touchscreen/intel-mid-touch.c864
-rw-r--r--drivers/staging/pohmelfs/inode.c6
-rw-r--r--drivers/staging/xgifb/TODO2
-rw-r--r--drivers/usb/core/inode.c1
-rw-r--r--drivers/usb/gadget/f_fs.c1
-rw-r--r--drivers/usb/gadget/inode.c1
-rw-r--r--drivers/usb/gadget/omap_udc.c18
-rw-r--r--drivers/usb/host/r8a66597-hcd.c4
-rw-r--r--drivers/uwb/Kconfig20
-rw-r--r--drivers/uwb/Makefile1
-rw-r--r--drivers/uwb/i1480/Makefile1
-rw-r--r--drivers/uwb/i1480/i1480-wlp.h200
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/Makefile8
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h283
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/lc.c424
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/netdev.c331
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/rx.c474
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/sysfs.c407
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/tx.c584
-rw-r--r--drivers/uwb/wlp/Makefile10
-rw-r--r--drivers/uwb/wlp/driver.c43
-rw-r--r--drivers/uwb/wlp/eda.c415
-rw-r--r--drivers/uwb/wlp/messages.c1798
-rw-r--r--drivers/uwb/wlp/sysfs.c708
-rw-r--r--drivers/uwb/wlp/txrx.c354
-rw-r--r--drivers/uwb/wlp/wlp-internal.h224
-rw-r--r--drivers/uwb/wlp/wlp-lc.c560
-rw-r--r--drivers/uwb/wlp/wss-lc.c959
-rw-r--r--drivers/video/Kconfig5
-rw-r--r--drivers/video/aty/atyfb_base.c3
-rw-r--r--drivers/video/au1200fb.c2
-rw-r--r--drivers/video/omap/lcd_omap3beagle.c2
-rw-r--r--drivers/video/omap2/displays/Kconfig2
-rw-r--r--drivers/video/omap2/displays/panel-acx565akm.c6
-rw-r--r--drivers/video/omap2/displays/panel-generic.c6
-rw-r--r--drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c6
-rw-r--r--drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c6
-rw-r--r--drivers/video/omap2/displays/panel-toppoly-tdo35s.c6
-rw-r--r--drivers/video/omap2/displays/panel-tpo-td043mtea1.c6
-rw-r--r--drivers/video/omap2/dss/Makefile2
-rw-r--r--drivers/video/omap2/dss/core.c3
-rw-r--r--drivers/video/omap2/dss/dispc.c270
-rw-r--r--drivers/video/omap2/dss/dsi.c1
-rw-r--r--drivers/video/omap2/dss/dss_features.c191
-rw-r--r--drivers/video/omap2/dss/dss_features.h50
-rw-r--r--drivers/video/omap2/dss/manager.c33
-rw-r--r--drivers/video/omap2/dss/overlay.c24
-rw-r--r--drivers/video/omap2/omapfb/Kconfig2
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c26
-rw-r--r--drivers/video/sh_mobile_hdmi.c84
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c6
-rw-r--r--drivers/video/xilinxfb.c24
-rw-r--r--drivers/watchdog/omap_wdt.c42
-rw-r--r--drivers/xen/events.c101
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c4
-rw-r--r--drivers/xen/xenfs/Makefile3
-rw-r--r--drivers/xen/xenfs/privcmd.c404
-rw-r--r--drivers/xen/xenfs/super.c95
-rw-r--r--drivers/xen/xenfs/xenfs.h3
-rw-r--r--drivers/xen/xenfs/xenstored.c68
669 files changed, 46218 insertions, 58917 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index a2aea53a75ed..14cf9077bb2b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -51,7 +51,6 @@ obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
obj-y += firewire/
-obj-y += ieee1394/
obj-$(CONFIG_UIO) += uio/
obj-y += cdrom/
obj-y += auxdisplay/
@@ -92,6 +91,7 @@ obj-$(CONFIG_EISA) += eisa/
obj-y += lguest/
obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_CPU_IDLE) += cpuidle/
+obj-$(CONFIG_DMA_ENGINE) += dma/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_MEMSTICK) += memstick/
obj-$(CONFIG_NEW_LEDS) += leds/
@@ -104,7 +104,6 @@ obj-$(CONFIG_ARCH_SHMOBILE) += sh/
ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
obj-y += clocksource/
endif
-obj-$(CONFIG_DMA_ENGINE) += dma/
obj-$(CONFIG_DCA) += dca/
obj-$(CONFIG_HID) += hid/
obj-$(CONFIG_PPC_PS3) += ps3/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 88681aca88c5..3f3489c5ca8c 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -9,7 +9,6 @@ menuconfig ACPI
depends on PCI
depends on PM
select PNP
- select CPU_IDLE
default y
help
Advanced Configuration and Power Interface (ACPI) support for
@@ -66,7 +65,6 @@ config ACPI_PROCFS
config ACPI_PROCFS_POWER
bool "Deprecated power /proc/acpi directories"
depends on PROC_FS
- default y
help
For backwards compatibility, this option allows
deprecated power /proc/acpi/ directories to exist, even when
@@ -90,13 +88,6 @@ config ACPI_POWER_METER
To compile this driver as a module, choose M here:
the module will be called power-meter.
-config ACPI_SYSFS_POWER
- bool "Future power /sys interface"
- select POWER_SUPPLY
- default y
- help
- Say N to disable power /sys interface
-
config ACPI_EC_DEBUGFS
tristate "EC read/write access through /sys/kernel/debug/ec"
default n
@@ -136,6 +127,7 @@ config ACPI_PROC_EVENT
config ACPI_AC
tristate "AC Adapter"
depends on X86
+ select POWER_SUPPLY
default y
help
This driver supports the AC Adapter object, which indicates
@@ -148,6 +140,7 @@ config ACPI_AC
config ACPI_BATTERY
tristate "Battery"
depends on X86
+ select POWER_SUPPLY
default y
help
This driver adds support for battery information through
@@ -206,6 +199,7 @@ config ACPI_DOCK
config ACPI_PROCESSOR
tristate "Processor"
select THERMAL
+ select CPU_IDLE
default y
help
This driver installs ACPI as the idle handler for Linux and uses
@@ -364,6 +358,7 @@ config ACPI_HOTPLUG_MEMORY
config ACPI_SBS
tristate "Smart Battery System"
depends on X86
+ select POWER_SUPPLY
help
This driver supports the Smart Battery System, another
type of access to battery information, found on some laptops.
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index 56205a0b85df..ba9afeaa23ac 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -32,9 +32,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
#include <linux/power_supply.h>
-#endif
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -86,9 +84,7 @@ static struct acpi_driver acpi_ac_driver = {
};
struct acpi_ac {
-#ifdef CONFIG_ACPI_SYSFS_POWER
struct power_supply charger;
-#endif
struct acpi_device * device;
unsigned long long state;
};
@@ -104,7 +100,6 @@ static const struct file_operations acpi_ac_fops = {
.release = single_release,
};
#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
static int get_ac_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -123,7 +118,6 @@ static int get_ac_property(struct power_supply *psy,
static enum power_supply_property ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
-#endif
/* --------------------------------------------------------------------------
AC Adapter Management
-------------------------------------------------------------------------- */
@@ -247,9 +241,7 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event)
dev_name(&device->dev), event,
(u32) ac->state);
acpi_notifier_call_chain(device, event, (u32) ac->state);
-#ifdef CONFIG_ACPI_SYSFS_POWER
kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
-#endif
}
return;
@@ -282,14 +274,12 @@ static int acpi_ac_add(struct acpi_device *device)
#endif
if (result)
goto end;
-#ifdef CONFIG_ACPI_SYSFS_POWER
ac->charger.name = acpi_device_bid(device);
ac->charger.type = POWER_SUPPLY_TYPE_MAINS;
ac->charger.properties = ac_props;
ac->charger.num_properties = ARRAY_SIZE(ac_props);
ac->charger.get_property = get_ac_property;
power_supply_register(&ac->device->dev, &ac->charger);
-#endif
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
acpi_device_name(device), acpi_device_bid(device),
@@ -316,10 +306,8 @@ static int acpi_ac_resume(struct acpi_device *device)
old_state = ac->state;
if (acpi_ac_get_state(ac))
return 0;
-#ifdef CONFIG_ACPI_SYSFS_POWER
if (old_state != ac->state)
kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
-#endif
return 0;
}
@@ -333,10 +321,8 @@ static int acpi_ac_remove(struct acpi_device *device, int type)
ac = acpi_driver_data(device);
-#ifdef CONFIG_ACPI_SYSFS_POWER
if (ac->charger.dev)
power_supply_unregister(&ac->charger);
-#endif
#ifdef CONFIG_ACPI_PROCFS_POWER
acpi_ac_remove_fs(device);
#endif
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
index d93cc06f4bf8..a7e1d1aa4107 100644
--- a/drivers/acpi/acpica/Makefile
+++ b/drivers/acpi/acpica/Makefile
@@ -21,7 +21,7 @@ acpi-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\
excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \
exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o exdebug.o
-acpi-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o
+acpi-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o hwpci.o
acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o
@@ -44,4 +44,5 @@ acpi-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o
acpi-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \
utcopy.o utdelete.o utglobal.o utmath.o utobject.o \
- utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o
+ utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o \
+ utosi.o utxferror.o
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index 48faf3eba9fb..72e9d5eb083c 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -105,6 +105,8 @@ void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg);
acpi_status
acpi_db_display_objects(char *obj_type_arg, char *display_count_arg);
+void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg);
+
acpi_status acpi_db_find_name_in_namespace(char *name_arg);
void acpi_db_set_scope(char *name);
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 36867cd70eac..a6f99cc37a19 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -105,8 +105,9 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
struct acpi_gpe_block_info **return_gpe_block);
acpi_status
-acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
- struct acpi_gpe_block_info *gpe_block);
+acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *ignored);
acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block);
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 1d192142c691..ad88fcae4eb9 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -132,6 +132,7 @@ struct acpi_table_fadt acpi_gbl_FADT;
u32 acpi_current_gpe_count;
u32 acpi_gbl_trace_flags;
acpi_name acpi_gbl_trace_method_name;
+u8 acpi_gbl_system_awake_and_running;
#endif
@@ -187,6 +188,10 @@ ACPI_EXTERN u8 acpi_gbl_integer_bit_width;
ACPI_EXTERN u8 acpi_gbl_integer_byte_width;
ACPI_EXTERN u8 acpi_gbl_integer_nybble_width;
+/* Mutex for _OSI support */
+
+ACPI_EXTERN acpi_mutex acpi_gbl_osi_mutex;
+
/* Reader/Writer lock is used for namespace walk and dynamic table unload */
ACPI_EXTERN struct acpi_rw_lock acpi_gbl_namespace_rw_lock;
@@ -255,6 +260,7 @@ ACPI_EXTERN acpi_init_handler acpi_gbl_init_handler;
ACPI_EXTERN acpi_tbl_handler acpi_gbl_table_handler;
ACPI_EXTERN void *acpi_gbl_table_handler_context;
ACPI_EXTERN struct acpi_walk_state *acpi_gbl_breakpoint_walk;
+ACPI_EXTERN acpi_interface_handler acpi_gbl_interface_handler;
/* Owner ID support */
@@ -273,8 +279,8 @@ ACPI_EXTERN u8 acpi_gbl_debugger_configuration;
ACPI_EXTERN u8 acpi_gbl_step_to_next_call;
ACPI_EXTERN u8 acpi_gbl_acpi_hardware_present;
ACPI_EXTERN u8 acpi_gbl_events_initialized;
-ACPI_EXTERN u8 acpi_gbl_system_awake_and_running;
ACPI_EXTERN u8 acpi_gbl_osi_data;
+ACPI_EXTERN struct acpi_interface_info *acpi_gbl_supported_interfaces;
#ifndef DEFINE_ACPI_GLOBALS
@@ -364,6 +370,7 @@ ACPI_EXTERN struct acpi_fixed_event_handler
ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head;
ACPI_EXTERN struct acpi_gpe_block_info
*acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS];
+ACPI_EXTERN u8 acpi_all_gpes_initialized;
/*****************************************************************************
*
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
index 120b3af56596..167470ad2d21 100644
--- a/drivers/acpi/acpica/achware.h
+++ b/drivers/acpi/acpica/achware.h
@@ -121,6 +121,13 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
struct acpi_gpe_block_info *gpe_block,
void *context);
+/*
+ * hwpci - PCI configuration support
+ */
+acpi_status
+acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
+ acpi_handle root_pci_device, acpi_handle pci_region);
+
#ifdef ACPI_FUTURE_USAGE
/*
* hwtimer - ACPI Timer prototypes
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 7dad9160f209..2ceb0c05b2d7 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -413,6 +413,7 @@ struct acpi_handler_info {
void *context; /* Context to be passed to handler */
struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */
u8 orig_flags; /* Original misc info about this GPE */
+ u8 orig_enabled; /* Set if the GPE was originally enabled */
};
union acpi_gpe_dispatch_info {
@@ -457,6 +458,7 @@ struct acpi_gpe_block_info {
u32 register_count; /* Number of register pairs in block */
u16 gpe_count; /* Number of individual GPEs in block */
u8 block_base_number; /* Base GPE number for this block */
+ u8 initialized; /* If set, the GPE block has been initialized */
};
/* Information about GPE interrupt handlers, one per each interrupt level used for GPEs */
@@ -473,7 +475,6 @@ struct acpi_gpe_walk_info {
struct acpi_gpe_block_info *gpe_block;
u16 count;
acpi_owner_id owner_id;
- u8 enable_this_gpe;
u8 execute_by_owner_id;
};
@@ -854,7 +855,7 @@ struct acpi_bit_register_info {
ACPI_BITMASK_POWER_BUTTON_STATUS | \
ACPI_BITMASK_SLEEP_BUTTON_STATUS | \
ACPI_BITMASK_RT_CLOCK_STATUS | \
- ACPI_BITMASK_PCIEXP_WAKE_DISABLE | \
+ ACPI_BITMASK_PCIEXP_WAKE_STATUS | \
ACPI_BITMASK_WAKE_STATUS)
#define ACPI_BITMASK_TIMER_ENABLE 0x0001
@@ -909,15 +910,21 @@ struct acpi_bit_register_info {
#define ACPI_OSI_WIN_VISTA 0x07
#define ACPI_OSI_WINSRV_2008 0x08
#define ACPI_OSI_WIN_VISTA_SP1 0x09
-#define ACPI_OSI_WIN_7 0x0A
+#define ACPI_OSI_WIN_VISTA_SP2 0x0A
+#define ACPI_OSI_WIN_7 0x0B
#define ACPI_ALWAYS_ILLEGAL 0x00
struct acpi_interface_info {
char *name;
+ struct acpi_interface_info *next;
+ u8 flags;
u8 value;
};
+#define ACPI_OSI_INVALID 0x01
+#define ACPI_OSI_DYNAMIC 0x02
+
struct acpi_port_info {
char *name;
u16 start;
@@ -997,7 +1004,7 @@ struct acpi_port_info {
struct acpi_db_method_info {
acpi_handle main_thread_gate;
acpi_handle thread_complete_gate;
- u32 *threads;
+ acpi_thread_id *threads;
u32 num_threads;
u32 num_created;
u32 num_completed;
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index 9894929a2abb..8d5c9e0a495f 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -338,8 +338,8 @@
* the plist contains a set of parens to allow variable-length lists.
* These macros are used for both the debug and non-debug versions of the code.
*/
-#define ACPI_ERROR_NAMESPACE(s, e) acpi_ns_report_error (AE_INFO, s, e);
-#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ns_report_method_error (AE_INFO, s, n, p, e);
+#define ACPI_ERROR_NAMESPACE(s, e) acpi_ut_namespace_error (AE_INFO, s, e);
+#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ut_method_error (AE_INFO, s, n, p, e);
#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist
#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 9f60ff002203..d44d3bc5b847 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -339,18 +339,6 @@ acpi_object_type acpi_ns_get_type(struct acpi_namespace_node *node);
u32 acpi_ns_local(acpi_object_type type);
void
-acpi_ns_report_error(const char *module_name,
- u32 line_number,
- const char *internal_name, acpi_status lookup_status);
-
-void
-acpi_ns_report_method_error(const char *module_name,
- u32 line_number,
- const char *message,
- struct acpi_namespace_node *node,
- const char *path, acpi_status lookup_status);
-
-void
acpi_ns_print_node_pathname(struct acpi_namespace_node *node, const char *msg);
acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info);
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 54857fa87aaf..bdbfaf22bd14 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -248,7 +248,7 @@ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO};
u32 base_byte_offset; /* Byte offset within containing object */\
u32 value; /* Value to store into the Bank or Index register */\
u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\
- u8 access_bit_width; /* Read/Write size in bits (8-64) */
+
struct acpi_object_field_common { /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */
ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 35df755251ce..72e4183c1937 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -312,8 +312,6 @@ void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list);
/*
* uteval - object evaluation
*/
-acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state);
-
acpi_status
acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node,
char *path,
@@ -395,6 +393,21 @@ acpi_status
acpi_ut_get_object_size(union acpi_operand_object *obj, acpi_size * obj_length);
/*
+ * utosi - Support for the _OSI predefined control method
+ */
+acpi_status acpi_ut_initialize_interfaces(void);
+
+void acpi_ut_interface_terminate(void);
+
+acpi_status acpi_ut_install_interface(acpi_string interface_name);
+
+acpi_status acpi_ut_remove_interface(acpi_string interface_name);
+
+struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name);
+
+acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state);
+
+/*
* utstate - Generic state creation/cache routines
*/
void
@@ -473,17 +486,6 @@ u8 acpi_ut_valid_acpi_char(char character, u32 position);
acpi_status acpi_ut_strtoul64(char *string, u32 base, u64 * ret_integer);
-void ACPI_INTERNAL_VAR_XFACE
-acpi_ut_predefined_warning(const char *module_name,
- u32 line_number,
- char *pathname,
- u8 node_flags, const char *format, ...);
-
-void ACPI_INTERNAL_VAR_XFACE
-acpi_ut_predefined_info(const char *module_name,
- u32 line_number,
- char *pathname, u8 node_flags, const char *format, ...);
-
/* Values for Base above (16=Hex, 10=Decimal) */
#define ACPI_ANY_BASE 0
@@ -574,6 +576,32 @@ acpi_status
acpi_ut_create_list(char *list_name,
u16 object_size, struct acpi_memory_list **return_cache);
-#endif
+#endif /* ACPI_DBG_TRACK_ALLOCATIONS */
+
+/*
+ * utxferror - various error/warning output functions
+ */
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_warning(const char *module_name,
+ u32 line_number,
+ char *pathname,
+ u8 node_flags, const char *format, ...);
+
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+ u32 line_number,
+ char *pathname, u8 node_flags, const char *format, ...);
+
+void
+acpi_ut_namespace_error(const char *module_name,
+ u32 line_number,
+ const char *internal_name, acpi_status lookup_status);
+
+void
+acpi_ut_method_error(const char *module_name,
+ u32 line_number,
+ const char *message,
+ struct acpi_namespace_node *node,
+ const char *path, acpi_status lookup_status);
#endif /* _ACUTILS_H */
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index 64750ee96e20..d94dd8974b55 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -573,7 +573,7 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
acpi_os_release_mutex(method_desc->method.
mutex->mutex.os_mutex);
- method_desc->method.mutex->mutex.thread_id = NULL;
+ method_desc->method.mutex->mutex.thread_id = 0;
}
}
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index d555b374e314..6b0b5d08d97a 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -300,10 +300,25 @@ acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state,
* we must enter this object into the namespace. The created
* object is temporary and will be deleted upon completion of
* the execution of this method.
+ *
+ * Note 10/2010: Except for the Scope() op. This opcode does
+ * not actually create a new object, it refers to an existing
+ * object. However, for Scope(), we want to indeed open a
+ * new scope.
*/
- status = acpi_ds_load2_begin_op(walk_state, NULL);
+ if (op->common.aml_opcode != AML_SCOPE_OP) {
+ status =
+ acpi_ds_load2_begin_op(walk_state, NULL);
+ } else {
+ status =
+ acpi_ds_scope_stack_push(op->named.node,
+ op->named.node->
+ type, walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ }
}
-
break;
case AML_CLASS_EXECUTE:
diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c
index 303618889da0..c61c3039c31a 100644
--- a/drivers/acpi/acpica/evevent.c
+++ b/drivers/acpi/acpica/evevent.c
@@ -95,47 +95,6 @@ acpi_status acpi_ev_initialize_events(void)
/*******************************************************************************
*
- * FUNCTION: acpi_ev_install_fadt_gpes
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Completes initialization of the FADT-defined GPE blocks
- * (0 and 1). The HW must be fully initialized at this point,
- * including global lock support.
- *
- ******************************************************************************/
-
-acpi_status acpi_ev_install_fadt_gpes(void)
-{
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(ev_install_fadt_gpes);
-
- /* Namespace must be locked */
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
-
- /* FADT GPE Block 0 */
-
- (void)acpi_ev_initialize_gpe_block(acpi_gbl_fadt_gpe_device,
- acpi_gbl_gpe_fadt_blocks[0]);
-
- /* FADT GPE Block 1 */
-
- (void)acpi_ev_initialize_gpe_block(acpi_gbl_fadt_gpe_device,
- acpi_gbl_gpe_fadt_blocks[1]);
-
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
- return_ACPI_STATUS(AE_OK);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_ev_install_xrupt_handlers
*
* PARAMETERS: None
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
index 85445fb5844e..020add3eee1c 100644
--- a/drivers/acpi/acpica/evgpeblk.c
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -363,6 +363,7 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH);
gpe_block->register_count = register_count;
gpe_block->block_base_number = gpe_block_base_number;
+ gpe_block->initialized = FALSE;
ACPI_MEMCPY(&gpe_block->block_address, gpe_block_address,
sizeof(struct acpi_generic_address));
@@ -385,11 +386,12 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
return_ACPI_STATUS(status);
}
+ acpi_all_gpes_initialized = FALSE;
+
/* Find all GPE methods (_Lxx or_Exx) for this block */
walk_info.gpe_block = gpe_block;
walk_info.gpe_device = gpe_device;
- walk_info.enable_this_gpe = FALSE;
walk_info.execute_by_owner_id = FALSE;
status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device,
@@ -434,35 +436,34 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
******************************************************************************/
acpi_status
-acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
- struct acpi_gpe_block_info *gpe_block)
+acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+ struct acpi_gpe_block_info *gpe_block,
+ void *ignored)
{
acpi_status status;
struct acpi_gpe_event_info *gpe_event_info;
u32 gpe_enabled_count;
u32 gpe_index;
- u32 gpe_number;
u32 i;
u32 j;
ACPI_FUNCTION_TRACE(ev_initialize_gpe_block);
- /* Ignore a null GPE block (e.g., if no GPE block 1 exists) */
-
- if (!gpe_block) {
+ /*
+ * Ignore a null GPE block (e.g., if no GPE block 1 exists) and
+ * GPE blocks that have been initialized already.
+ */
+ if (!gpe_block || gpe_block->initialized) {
return_ACPI_STATUS(AE_OK);
}
/*
- * Enable all GPEs that have a corresponding method. Any other GPEs
- * within this block must be enabled via the acpi_enable_gpe interface.
+ * Enable all GPEs that have a corresponding method and have the
+ * ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block must
+ * be enabled via the acpi_enable_gpe() interface.
*/
gpe_enabled_count = 0;
- if (gpe_device == acpi_gbl_fadt_gpe_device) {
- gpe_device = NULL;
- }
-
for (i = 0; i < gpe_block->register_count; i++) {
for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
@@ -470,27 +471,19 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j;
gpe_event_info = &gpe_block->event_info[gpe_index];
- gpe_number = gpe_index + gpe_block->block_base_number;
/* Ignore GPEs that have no corresponding _Lxx/_Exx method */
- if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)) {
+ if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)
+ || (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {
continue;
}
- /*
- * If the GPE has already been enabled for runtime
- * signaling, make sure it remains enabled, but do not
- * increment its reference counter.
- */
- status = gpe_event_info->runtime_count ?
- acpi_ev_enable_gpe(gpe_event_info) :
- acpi_enable_gpe(gpe_device, gpe_number);
-
+ status = acpi_raw_enable_gpe(gpe_event_info);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
- "Could not enable GPE 0x%02X",
- gpe_number));
+ "Could not enable GPE 0x%02X",
+ gpe_index + gpe_block->block_base_number));
continue;
}
@@ -504,5 +497,7 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
gpe_enabled_count));
}
+ gpe_block->initialized = TRUE;
+
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c
index 3084c5de1bba..2c7def95f721 100644
--- a/drivers/acpi/acpica/evgpeinit.c
+++ b/drivers/acpi/acpica/evgpeinit.c
@@ -210,8 +210,7 @@ acpi_status acpi_ev_gpe_initialize(void)
*
* DESCRIPTION: Check for new GPE methods (_Lxx/_Exx) made available as a
* result of a Load() or load_table() operation. If new GPE
- * methods have been installed, register the new methods and
- * enable and runtime GPEs that are associated with them.
+ * methods have been installed, register the new methods.
*
******************************************************************************/
@@ -239,7 +238,6 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id)
walk_info.owner_id = table_owner_id;
walk_info.execute_by_owner_id = TRUE;
walk_info.count = 0;
- walk_info.enable_this_gpe = TRUE;
/* Walk the interrupt level descriptor list */
@@ -301,8 +299,6 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id)
*
* If walk_info->execute_by_owner_id is TRUE, we only execute examine GPE methods
* with that owner.
- * If walk_info->enable_this_gpe is TRUE, the GPE that is referred to by a GPE
- * method is immediately enabled (Used for Load/load_table operators)
*
******************************************************************************/
@@ -315,8 +311,6 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
struct acpi_gpe_walk_info *walk_info =
ACPI_CAST_PTR(struct acpi_gpe_walk_info, context);
struct acpi_gpe_event_info *gpe_event_info;
- struct acpi_namespace_node *gpe_device;
- acpi_status status;
u32 gpe_number;
char name[ACPI_NAME_SIZE + 1];
u8 type;
@@ -421,29 +415,6 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD);
gpe_event_info->dispatch.method_node = method_node;
- /*
- * Enable this GPE if requested. This only happens when during the
- * execution of a Load or load_table operator. We have found a new
- * GPE method and want to immediately enable the GPE if it is a
- * runtime GPE.
- */
- if (walk_info->enable_this_gpe) {
-
- walk_info->count++;
- gpe_device = walk_info->gpe_device;
-
- if (gpe_device == acpi_gbl_fadt_gpe_device) {
- gpe_device = NULL;
- }
-
- status = acpi_enable_gpe(gpe_device, gpe_number);
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "Could not enable GPE 0x%02X",
- gpe_number));
- }
- }
-
ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
"Registered GPE method %s as GPE number 0x%.2X\n",
name, gpe_number));
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
index df0aea9a8cfd..fcaed9fb44ff 100644
--- a/drivers/acpi/acpica/evmisc.c
+++ b/drivers/acpi/acpica/evmisc.c
@@ -553,7 +553,7 @@ acpi_status acpi_ev_release_global_lock(void)
acpi_gbl_global_lock_acquired = FALSE;
/* Release the local GL mutex */
- acpi_ev_global_lock_thread_id = NULL;
+ acpi_ev_global_lock_thread_id = 0;
acpi_ev_global_lock_acquired = 0;
acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
index f40d271bf568..0b47a6dc9290 100644
--- a/drivers/acpi/acpica/evrgnini.c
+++ b/drivers/acpi/acpica/evrgnini.c
@@ -289,8 +289,8 @@ acpi_ev_pci_config_region_setup(acpi_handle handle,
}
/*
- * Get the PCI device and function numbers from the _ADR object contained
- * in the parent's scope.
+ * Get the PCI device and function numbers from the _ADR object
+ * contained in the parent's scope.
*/
status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
pci_device_node, &pci_value);
@@ -320,9 +320,15 @@ acpi_ev_pci_config_region_setup(acpi_handle handle,
pci_id->bus = ACPI_LOWORD(pci_value);
}
- /* Complete this device's pci_id */
+ /* Complete/update the PCI ID for this device */
- acpi_os_derive_pci_id(pci_root_node, region_obj->region.node, &pci_id);
+ status =
+ acpi_hw_derive_pci_id(pci_id, pci_root_node,
+ region_obj->region.node);
+ if (ACPI_FAILURE(status)) {
+ ACPI_FREE(pci_id);
+ return_ACPI_STATUS(status);
+ }
*region_context = pci_id;
return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 14e48add32fa..36af222cac65 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -726,15 +726,16 @@ acpi_install_gpe_handler(acpi_handle gpe_device,
(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK);
/*
- * If the GPE is associated with a method and it cannot wake up the
- * system from sleep states, it was enabled automatically during
- * initialization, so it has to be disabled now to avoid spurious
- * execution of the handler.
+ * If the GPE is associated with a method, it might have been enabled
+ * automatically during initialization, in which case it has to be
+ * disabled now to avoid spurious execution of the handler.
*/
if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD)
- && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE))
+ && gpe_event_info->runtime_count) {
+ handler->orig_enabled = 1;
(void)acpi_raw_disable_gpe(gpe_event_info);
+ }
/* Install the handler */
@@ -837,13 +838,13 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
gpe_event_info->flags |= handler->orig_flags;
/*
- * If the GPE was previously associated with a method and it cannot wake
- * up the system from sleep states, it should be enabled at this point
- * to restore the post-initialization configuration.
+ * If the GPE was previously associated with a method and it was
+ * enabled, it should be enabled at this point to restore the
+ * post-initialization configuration.
*/
if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD)
- && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE))
+ && handler->orig_enabled)
(void)acpi_raw_enable_gpe(gpe_event_info);
/* Now we can free the handler object */
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index 304825528d48..a1dabe3fd8ae 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -379,21 +379,12 @@ acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number)
/* Ensure that we have a valid GPE number */
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
- if (!gpe_event_info) {
+ if (gpe_event_info) {
+ gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
+ } else {
status = AE_BAD_PARAMETER;
- goto unlock_and_exit;
- }
-
- if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) {
- goto unlock_and_exit;
}
- gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
- if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) {
- (void)acpi_raw_disable_gpe(gpe_event_info);
- }
-
-unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return_ACPI_STATUS(status);
}
@@ -651,7 +642,7 @@ acpi_install_gpe_block(acpi_handle gpe_device,
struct acpi_generic_address *gpe_block_address,
u32 register_count, u32 interrupt_number)
{
- acpi_status status;
+ acpi_status status = AE_OK;
union acpi_operand_object *obj_desc;
struct acpi_namespace_node *node;
struct acpi_gpe_block_info *gpe_block;
@@ -715,10 +706,6 @@ acpi_install_gpe_block(acpi_handle gpe_device,
obj_desc->device.gpe_block = gpe_block;
- /* Enable the runtime GPEs in the new block */
-
- status = acpi_ev_initialize_gpe_block(node, gpe_block);
-
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(status);
@@ -924,3 +911,43 @@ acpi_status acpi_enable_all_runtime_gpes(void)
return_ACPI_STATUS(status);
}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_update_gpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Enable all GPEs that have associated _Lxx or _Exx methods and
+ * are not pointed to by any device _PRW methods indicating that
+ * these GPEs are generally intended for system or device wakeup
+ * (such GPEs have to be enabled directly when the devices whose
+ * _PRW methods point to them are set up for wakeup signaling).
+ *
+ ******************************************************************************/
+
+acpi_status acpi_update_gpes(void)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_update_gpes);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ } else if (acpi_all_gpes_initialized) {
+ goto unlock;
+ }
+
+ status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL);
+ if (ACPI_SUCCESS(status)) {
+ acpi_all_gpes_initialized = TRUE;
+ }
+
+unlock:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 541cbc1544d5..ce9314f79451 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -64,6 +64,12 @@ ACPI_MODULE_NAME("evxfregn")
*
* DESCRIPTION: Install a handler for all op_regions of a given space_id.
*
+ * NOTE: This function should only be called after acpi_enable_subsystem has
+ * been called. This is because any _REG methods associated with the Space ID
+ * are executed here, and these methods can only be safely executed after
+ * the default handlers have been installed and the hardware has been
+ * initialized (via acpi_enable_subsystem.)
+ *
******************************************************************************/
acpi_status
acpi_install_address_space_handler(acpi_handle device,
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
index 047217303a4b..38293fd3e088 100644
--- a/drivers/acpi/acpica/exfldio.c
+++ b/drivers/acpi/acpica/exfldio.c
@@ -119,8 +119,8 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc,
}
/*
- * Exit now for SMBus or IPMI address space, it has a non-linear address space
- * and the request cannot be directly validated
+ * Exit now for SMBus or IPMI address space, it has a non-linear
+ * address space and the request cannot be directly validated
*/
if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS ||
rgn_desc->region.space_id == ACPI_ADR_SPACE_IPMI) {
@@ -147,8 +147,7 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc,
* (Region length is specified in bytes)
*/
if (rgn_desc->region.length <
- (obj_desc->common_field.base_byte_offset +
- field_datum_byte_offset +
+ (obj_desc->common_field.base_byte_offset + field_datum_byte_offset +
obj_desc->common_field.access_byte_width)) {
if (acpi_gbl_enable_interpreter_slack) {
/*
@@ -680,6 +679,7 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
u32 buffer_tail_bits;
u32 datum_count;
u32 field_datum_count;
+ u32 access_bit_width;
u32 i;
ACPI_FUNCTION_TRACE(ex_extract_from_field);
@@ -694,16 +694,36 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
}
+
ACPI_MEMSET(buffer, 0, buffer_length);
+ access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
+
+ /* Handle the simple case here */
+
+ if ((obj_desc->common_field.start_field_bit_offset == 0) &&
+ (obj_desc->common_field.bit_length == access_bit_width)) {
+ status = acpi_ex_field_datum_io(obj_desc, 0, buffer, ACPI_READ);
+ return_ACPI_STATUS(status);
+ }
+
+/* TBD: Move to common setup code */
+
+ /* Field algorithm is limited to sizeof(u64), truncate if needed */
+
+ if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
+ obj_desc->common_field.access_byte_width = sizeof(u64);
+ access_bit_width = sizeof(u64) * 8;
+ }
/* Compute the number of datums (access width data items) */
- datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
- obj_desc->common_field.access_bit_width);
+ datum_count =
+ ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
+ access_bit_width);
+
field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
obj_desc->common_field.
start_field_bit_offset,
- obj_desc->common_field.
access_bit_width);
/* Priming read from the field */
@@ -738,12 +758,11 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
* This avoids the differences in behavior between different compilers
* concerning shift values larger than the target data width.
*/
- if ((obj_desc->common_field.access_bit_width -
- obj_desc->common_field.start_field_bit_offset) <
+ if (access_bit_width -
+ obj_desc->common_field.start_field_bit_offset <
ACPI_INTEGER_BIT_SIZE) {
merged_datum |=
- raw_datum << (obj_desc->common_field.
- access_bit_width -
+ raw_datum << (access_bit_width -
obj_desc->common_field.
start_field_bit_offset);
}
@@ -765,8 +784,7 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
/* Mask off any extra bits in the last datum */
- buffer_tail_bits = obj_desc->common_field.bit_length %
- obj_desc->common_field.access_bit_width;
+ buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width;
if (buffer_tail_bits) {
merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
}
@@ -798,6 +816,7 @@ acpi_status
acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
void *buffer, u32 buffer_length)
{
+ void *new_buffer;
acpi_status status;
u64 mask;
u64 width_mask;
@@ -808,9 +827,9 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
u32 buffer_tail_bits;
u32 datum_count;
u32 field_datum_count;
- u32 i;
+ u32 access_bit_width;
u32 required_length;
- void *new_buffer;
+ u32 i;
ACPI_FUNCTION_TRACE(ex_insert_into_field);
@@ -844,17 +863,24 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
buffer_length = required_length;
}
+/* TBD: Move to common setup code */
+
+ /* Algo is limited to sizeof(u64), so cut the access_byte_width */
+ if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
+ obj_desc->common_field.access_byte_width = sizeof(u64);
+ }
+
+ access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
+
/*
* Create the bitmasks used for bit insertion.
* Note: This if/else is used to bypass compiler differences with the
* shift operator
*/
- if (obj_desc->common_field.access_bit_width == ACPI_INTEGER_BIT_SIZE) {
+ if (access_bit_width == ACPI_INTEGER_BIT_SIZE) {
width_mask = ACPI_UINT64_MAX;
} else {
- width_mask =
- ACPI_MASK_BITS_ABOVE(obj_desc->common_field.
- access_bit_width);
+ width_mask = ACPI_MASK_BITS_ABOVE(access_bit_width);
}
mask = width_mask &
@@ -863,12 +889,11 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
/* Compute the number of datums (access width data items) */
datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
- obj_desc->common_field.access_bit_width);
+ access_bit_width);
field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
obj_desc->common_field.
start_field_bit_offset,
- obj_desc->common_field.
access_bit_width);
/* Get initial Datum from the input buffer */
@@ -905,12 +930,11 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
* This avoids the differences in behavior between different compilers
* concerning shift values larger than the target data width.
*/
- if ((obj_desc->common_field.access_bit_width -
+ if ((access_bit_width -
obj_desc->common_field.start_field_bit_offset) <
ACPI_INTEGER_BIT_SIZE) {
merged_datum =
- raw_datum >> (obj_desc->common_field.
- access_bit_width -
+ raw_datum >> (access_bit_width -
obj_desc->common_field.
start_field_bit_offset);
} else {
@@ -929,6 +953,7 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
ACPI_MEMCPY(&raw_datum, ((char *)buffer) + buffer_offset,
ACPI_MIN(obj_desc->common_field.access_byte_width,
buffer_length - buffer_offset));
+
merged_datum |=
raw_datum << obj_desc->common_field.start_field_bit_offset;
}
@@ -937,7 +962,7 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
buffer_tail_bits = (obj_desc->common_field.bit_length +
obj_desc->common_field.start_field_bit_offset) %
- obj_desc->common_field.access_bit_width;
+ access_bit_width;
if (buffer_tail_bits) {
mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
}
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
index f73be97043c0..6af14e43f839 100644
--- a/drivers/acpi/acpica/exmutex.c
+++ b/drivers/acpi/acpica/exmutex.c
@@ -336,7 +336,7 @@ acpi_status acpi_ex_release_mutex_object(union acpi_operand_object *obj_desc)
/* Clear mutex info */
- obj_desc->mutex.thread_id = NULL;
+ obj_desc->mutex.thread_id = 0;
return_ACPI_STATUS(status);
}
@@ -393,10 +393,10 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
if ((owner_thread->thread_id != walk_state->thread->thread_id) &&
(obj_desc != acpi_gbl_global_lock_mutex)) {
ACPI_ERROR((AE_INFO,
- "Thread %p cannot release Mutex [%4.4s] acquired by thread %p",
- ACPI_CAST_PTR(void, walk_state->thread->thread_id),
+ "Thread %u cannot release Mutex [%4.4s] acquired by thread %u",
+ (u32)walk_state->thread->thread_id,
acpi_ut_get_node_name(obj_desc->mutex.node),
- ACPI_CAST_PTR(void, owner_thread->thread_id)));
+ (u32)owner_thread->thread_id));
return_ACPI_STATUS(AE_AML_NOT_OWNER);
}
@@ -488,7 +488,7 @@ void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread)
/* Mark mutex unowned */
obj_desc->mutex.owner_thread = NULL;
- obj_desc->mutex.thread_id = NULL;
+ obj_desc->mutex.thread_id = 0;
/* Update Thread sync_level (Last mutex is the important one) */
diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c
index 98a331d2249b..7aae29f73d3f 100644
--- a/drivers/acpi/acpica/exprep.c
+++ b/drivers/acpi/acpica/exprep.c
@@ -355,12 +355,10 @@ acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc,
return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
}
- /* Setup width (access granularity) fields */
+ /* Setup width (access granularity) fields (values are: 1, 2, 4, 8) */
obj_desc->common_field.access_byte_width = (u8)
- ACPI_DIV_8(access_bit_width); /* 1, 2, 4, 8 */
-
- obj_desc->common_field.access_bit_width = (u8) access_bit_width;
+ ACPI_DIV_8(access_bit_width);
/*
* base_byte_offset is the address of the start of the field within the
@@ -405,8 +403,9 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
{
union acpi_operand_object *obj_desc;
union acpi_operand_object *second_desc = NULL;
- u32 type;
acpi_status status;
+ u32 access_byte_width;
+ u32 type;
ACPI_FUNCTION_TRACE(ex_prep_field_value);
@@ -421,8 +420,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
type = acpi_ns_get_type(info->region_node);
if (type != ACPI_TYPE_REGION) {
ACPI_ERROR((AE_INFO,
- "Needed Region, found type 0x%X (%s)",
- type, acpi_ut_get_type_name(type)));
+ "Needed Region, found type 0x%X (%s)", type,
+ acpi_ut_get_type_name(type)));
return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
}
@@ -438,7 +437,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
/* Initialize areas of the object that are common to all fields */
obj_desc->common_field.node = info->field_node;
- status = acpi_ex_prep_common_field_object(obj_desc, info->field_flags,
+ status = acpi_ex_prep_common_field_object(obj_desc,
+ info->field_flags,
info->attribute,
info->field_bit_position,
info->field_bit_length);
@@ -455,26 +455,25 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
obj_desc->field.region_obj =
acpi_ns_get_attached_object(info->region_node);
- /* An additional reference for the container */
+ /* Allow full data read from EC address space */
- acpi_ut_add_reference(obj_desc->field.region_obj);
+ if ((obj_desc->field.region_obj->region.space_id ==
+ ACPI_ADR_SPACE_EC)
+ && (obj_desc->common_field.bit_length > 8)) {
+ access_byte_width =
+ ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.
+ bit_length);
+
+ /* Maximum byte width supported is 255 */
- /* allow full data read from EC address space */
- if (obj_desc->field.region_obj->region.space_id ==
- ACPI_ADR_SPACE_EC) {
- if (obj_desc->common_field.bit_length > 8) {
- unsigned width =
- ACPI_ROUND_BITS_UP_TO_BYTES(
- obj_desc->common_field.bit_length);
- // access_bit_width is u8, don't overflow it
- if (width > 8)
- width = 8;
+ if (access_byte_width < 256) {
obj_desc->common_field.access_byte_width =
- width;
- obj_desc->common_field.access_bit_width =
- 8 * width;
+ (u8)access_byte_width;
}
}
+ /* An additional reference for the container */
+
+ acpi_ut_add_reference(obj_desc->field.region_obj);
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
"RegionField: BitOff %X, Off %X, Gran %X, Region %p\n",
diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c
index 8819d2ac5aee..de17e10da0ed 100644
--- a/drivers/acpi/acpica/exregion.c
+++ b/drivers/acpi/acpica/exregion.c
@@ -353,7 +353,6 @@ acpi_ex_pci_config_space_handler(u32 function,
acpi_status status = AE_OK;
struct acpi_pci_id *pci_id;
u16 pci_register;
- u32 value32;
ACPI_FUNCTION_TRACE(ex_pci_config_space_handler);
@@ -381,8 +380,7 @@ acpi_ex_pci_config_space_handler(u32 function,
case ACPI_READ:
status = acpi_os_read_pci_configuration(pci_id, pci_register,
- &value32, bit_width);
- *value = value32;
+ value, bit_width);
break;
case ACPI_WRITE:
diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c
new file mode 100644
index 000000000000..ad21c7d8bf4f
--- /dev/null
+++ b/drivers/acpi/acpica/hwpci.c
@@ -0,0 +1,412 @@
+/*******************************************************************************
+ *
+ * Module Name: hwpci - Obtain PCI bus, device, and function numbers
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2010, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("hwpci")
+
+/* PCI configuration space values */
+#define PCI_CFG_HEADER_TYPE_REG 0x0E
+#define PCI_CFG_PRIMARY_BUS_NUMBER_REG 0x18
+#define PCI_CFG_SECONDARY_BUS_NUMBER_REG 0x19
+/* PCI header values */
+#define PCI_HEADER_TYPE_MASK 0x7F
+#define PCI_TYPE_BRIDGE 0x01
+#define PCI_TYPE_CARDBUS_BRIDGE 0x02
+typedef struct acpi_pci_device {
+ acpi_handle device;
+ struct acpi_pci_device *next;
+
+} acpi_pci_device;
+
+/* Local prototypes */
+
+static acpi_status
+acpi_hw_build_pci_list(acpi_handle root_pci_device,
+ acpi_handle pci_region,
+ struct acpi_pci_device **return_list_head);
+
+static acpi_status
+acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
+ struct acpi_pci_device *list_head);
+
+static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head);
+
+static acpi_status
+acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
+ acpi_handle pci_device,
+ u16 *bus_number, u8 *is_bridge);
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_derive_pci_id
+ *
+ * PARAMETERS: pci_id - Initial values for the PCI ID. May be
+ * modified by this function.
+ * root_pci_device - A handle to a PCI device object. This
+ * object must be a PCI Root Bridge having a
+ * _HID value of either PNP0A03 or PNP0A08
+ * pci_region - A handle to a PCI configuration space
+ * Operation Region being initialized
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function derives a full PCI ID for a PCI device,
+ * consisting of a Segment number, Bus number, Device number,
+ * and function code.
+ *
+ * The PCI hardware dynamically configures PCI bus numbers
+ * depending on the bus topology discovered during system
+ * initialization. This function is invoked during configuration
+ * of a PCI_Config Operation Region in order to (possibly) update
+ * the Bus/Device/Function numbers in the pci_id with the actual
+ * values as determined by the hardware and operating system
+ * configuration.
+ *
+ * The pci_id parameter is initially populated during the Operation
+ * Region initialization. This function is then called, and is
+ * will make any necessary modifications to the Bus, Device, or
+ * Function number PCI ID subfields as appropriate for the
+ * current hardware and OS configuration.
+ *
+ * NOTE: Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id
+ * interface since this feature is OS-independent. This module
+ * specifically avoids any use of recursion by building a local
+ * temporary device list.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
+ acpi_handle root_pci_device, acpi_handle pci_region)
+{
+ acpi_status status;
+ struct acpi_pci_device *list_head = NULL;
+
+ ACPI_FUNCTION_TRACE(hw_derive_pci_id);
+
+ if (!pci_id) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ /* Build a list of PCI devices, from pci_region up to root_pci_device */
+
+ status =
+ acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head);
+ if (ACPI_SUCCESS(status)) {
+
+ /* Walk the list, updating the PCI device/function/bus numbers */
+
+ status = acpi_hw_process_pci_list(pci_id, list_head);
+ }
+
+ /* Always delete the list */
+
+ acpi_hw_delete_pci_list(list_head);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_build_pci_list
+ *
+ * PARAMETERS: root_pci_device - A handle to a PCI device object. This
+ * object is guaranteed to be a PCI Root
+ * Bridge having a _HID value of either
+ * PNP0A03 or PNP0A08
+ * pci_region - A handle to the PCI configuration space
+ * Operation Region
+ * return_list_head - Where the PCI device list is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Builds a list of devices from the input PCI region up to the
+ * Root PCI device for this namespace subtree.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_build_pci_list(acpi_handle root_pci_device,
+ acpi_handle pci_region,
+ struct acpi_pci_device **return_list_head)
+{
+ acpi_handle current_device;
+ acpi_handle parent_device;
+ acpi_status status;
+ struct acpi_pci_device *list_element;
+ struct acpi_pci_device *list_head = NULL;
+
+ /*
+ * Ascend namespace branch until the root_pci_device is reached, building
+ * a list of device nodes. Loop will exit when either the PCI device is
+ * found, or the root of the namespace is reached.
+ */
+ current_device = pci_region;
+ while (1) {
+ status = acpi_get_parent(current_device, &parent_device);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
+
+ if (parent_device == root_pci_device) {
+ *return_list_head = list_head;
+ return (AE_OK);
+ }
+
+ list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));
+ if (!list_element) {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Put new element at the head of the list */
+
+ list_element->next = list_head;
+ list_element->device = parent_device;
+ list_head = list_element;
+
+ current_device = parent_device;
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_process_pci_list
+ *
+ * PARAMETERS: pci_id - Initial values for the PCI ID. May be
+ * modified by this function.
+ * list_head - Device list created by
+ * acpi_hw_build_pci_list
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Walk downward through the PCI device list, getting the device
+ * info for each, via the PCI configuration space and updating
+ * the PCI ID as necessary. Deletes the list during traversal.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_process_pci_list(struct acpi_pci_id *pci_id,
+ struct acpi_pci_device *list_head)
+{
+ acpi_status status = AE_OK;
+ struct acpi_pci_device *info;
+ u16 bus_number;
+ u8 is_bridge = TRUE;
+
+ ACPI_FUNCTION_NAME(hw_process_pci_list);
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Input PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n",
+ pci_id->segment, pci_id->bus, pci_id->device,
+ pci_id->function));
+
+ bus_number = pci_id->bus;
+
+ /*
+ * Descend down the namespace tree, collecting PCI device, function,
+ * and bus numbers. bus_number is only important for PCI bridges.
+ * Algorithm: As we descend the tree, use the last valid PCI device,
+ * function, and bus numbers that are discovered, and assign them
+ * to the PCI ID for the target device.
+ */
+ info = list_head;
+ while (info) {
+ status = acpi_hw_get_pci_device_info(pci_id, info->device,
+ &bus_number, &is_bridge);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ info = info->next;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X "
+ "Status %X BusNumber %X IsBridge %X\n",
+ pci_id->segment, pci_id->bus, pci_id->device,
+ pci_id->function, status, bus_number, is_bridge));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_delete_pci_list
+ *
+ * PARAMETERS: list_head - Device list created by
+ * acpi_hw_build_pci_list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Free the entire PCI list.
+ *
+ ******************************************************************************/
+
+static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head)
+{
+ struct acpi_pci_device *next;
+ struct acpi_pci_device *previous;
+
+ next = list_head;
+ while (next) {
+ previous = next;
+ next = previous->next;
+ ACPI_FREE(previous);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_get_pci_device_info
+ *
+ * PARAMETERS: pci_id - Initial values for the PCI ID. May be
+ * modified by this function.
+ * pci_device - Handle for the PCI device object
+ * bus_number - Where a PCI bridge bus number is returned
+ * is_bridge - Return value, indicates if this PCI
+ * device is a PCI bridge
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get the device info for a single PCI device object. Get the
+ * _ADR (contains PCI device and function numbers), and for PCI
+ * bridge devices, get the bus number from PCI configuration
+ * space.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id,
+ acpi_handle pci_device,
+ u16 *bus_number, u8 *is_bridge)
+{
+ acpi_status status;
+ acpi_object_type object_type;
+ u64 return_value;
+ u64 pci_value;
+
+ /* We only care about objects of type Device */
+
+ status = acpi_get_type(pci_device, &object_type);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (object_type != ACPI_TYPE_DEVICE) {
+ return (AE_OK);
+ }
+
+ /* We need an _ADR. Ignore device if not present */
+
+ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR,
+ pci_device, &return_value);
+ if (ACPI_FAILURE(status)) {
+ return (AE_OK);
+ }
+
+ /*
+ * From _ADR, get the PCI Device and Function and
+ * update the PCI ID.
+ */
+ pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value));
+ pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value));
+
+ /*
+ * If the previous device was a bridge, use the previous
+ * device bus number
+ */
+ if (*is_bridge) {
+ pci_id->bus = *bus_number;
+ }
+
+ /*
+ * Get the bus numbers from PCI Config space:
+ *
+ * First, get the PCI header_type
+ */
+ *is_bridge = FALSE;
+ status = acpi_os_read_pci_configuration(pci_id,
+ PCI_CFG_HEADER_TYPE_REG,
+ &pci_value, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */
+
+ pci_value &= PCI_HEADER_TYPE_MASK;
+
+ if ((pci_value != PCI_TYPE_BRIDGE) &&
+ (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) {
+ return (AE_OK);
+ }
+
+ /* Bridge: Get the Primary bus_number */
+
+ status = acpi_os_read_pci_configuration(pci_id,
+ PCI_CFG_PRIMARY_BUS_NUMBER_REG,
+ &pci_value, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ *is_bridge = TRUE;
+ pci_id->bus = (u16)pci_value;
+
+ /* Bridge: Get the Secondary bus_number */
+
+ status = acpi_os_read_pci_configuration(pci_id,
+ PCI_CFG_SECONDARY_BUS_NUMBER_REG,
+ &pci_value, 8);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ *bus_number = (u16)pci_value;
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
index 4009498fbabd..4ef9f43ea926 100644
--- a/drivers/acpi/acpica/nsrepair2.c
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -74,10 +74,18 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
static acpi_status
+acpi_ns_repair_CID(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
acpi_ns_repair_FDE(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
static acpi_status
+acpi_ns_repair_HID(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
acpi_ns_repair_PSS(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
@@ -108,8 +116,10 @@ acpi_ns_sort_list(union acpi_operand_object **elements,
* As necessary:
*
* _ALR: Sort the list ascending by ambient_illuminance
+ * _CID: Strings: uppercase all, remove any leading asterisk
* _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs
* _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs
+ * _HID: Strings: uppercase all, remove any leading asterisk
* _PSS: Sort the list descending by Power
* _TSS: Sort the list descending by Power
*
@@ -122,8 +132,10 @@ acpi_ns_sort_list(union acpi_operand_object **elements,
*/
static const struct acpi_repair_info acpi_ns_repairable_names[] = {
{"_ALR", acpi_ns_repair_ALR},
+ {"_CID", acpi_ns_repair_CID},
{"_FDE", acpi_ns_repair_FDE},
{"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */
+ {"_HID", acpi_ns_repair_HID},
{"_PSS", acpi_ns_repair_PSS},
{"_TSS", acpi_ns_repair_TSS},
{{0, 0, 0, 0}, NULL} /* Table terminator */
@@ -321,6 +333,157 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data,
/******************************************************************************
*
+ * FUNCTION: acpi_ns_repair_CID
+ *
+ * PARAMETERS: Data - Pointer to validation data structure
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _CID object. If a string, ensure that all
+ * letters are uppercase and that there is no leading asterisk.
+ * If a Package, ensure same for all string elements.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_CID(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr)
+{
+ acpi_status status;
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object **element_ptr;
+ union acpi_operand_object *original_element;
+ u16 original_ref_count;
+ u32 i;
+
+ /* Check for _CID as a simple string */
+
+ if (return_object->common.type == ACPI_TYPE_STRING) {
+ status = acpi_ns_repair_HID(data, return_object_ptr);
+ return (status);
+ }
+
+ /* Exit if not a Package */
+
+ if (return_object->common.type != ACPI_TYPE_PACKAGE) {
+ return (AE_OK);
+ }
+
+ /* Examine each element of the _CID package */
+
+ element_ptr = return_object->package.elements;
+ for (i = 0; i < return_object->package.count; i++) {
+ original_element = *element_ptr;
+ original_ref_count = original_element->common.reference_count;
+
+ status = acpi_ns_repair_HID(data, element_ptr);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Take care with reference counts */
+
+ if (original_element != *element_ptr) {
+
+ /* Element was replaced */
+
+ (*element_ptr)->common.reference_count =
+ original_ref_count;
+
+ acpi_ut_remove_reference(original_element);
+ }
+
+ element_ptr++;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_HID
+ *
+ * PARAMETERS: Data - Pointer to validation data structure
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _HID object. If a string, ensure that all
+ * letters are uppercase and that there is no leading asterisk.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_HID(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object *new_string;
+ char *source;
+ char *dest;
+
+ ACPI_FUNCTION_NAME(ns_repair_HID);
+
+ /* We only care about string _HID objects (not integers) */
+
+ if (return_object->common.type != ACPI_TYPE_STRING) {
+ return (AE_OK);
+ }
+
+ if (return_object->string.length == 0) {
+ ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+ "Invalid zero-length _HID or _CID string"));
+
+ /* Return AE_OK anyway, let driver handle it */
+
+ data->flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
+ }
+
+ /* It is simplest to always create a new string object */
+
+ new_string = acpi_ut_create_string_object(return_object->string.length);
+ if (!new_string) {
+ return (AE_NO_MEMORY);
+ }
+
+ /*
+ * Remove a leading asterisk if present. For some unknown reason, there
+ * are many machines in the field that contains IDs like this.
+ *
+ * Examples: "*PNP0C03", "*ACPI0003"
+ */
+ source = return_object->string.pointer;
+ if (*source == '*') {
+ source++;
+ new_string->string.length--;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Removed invalid leading asterisk\n",
+ data->pathname));
+ }
+
+ /*
+ * Copy and uppercase the string. From the ACPI specification:
+ *
+ * A valid PNP ID must be of the form "AAA####" where A is an uppercase
+ * letter and # is a hex digit. A valid ACPI ID must be of the form
+ * "ACPI####" where # is a hex digit.
+ */
+ for (dest = new_string->string.pointer; *source; dest++, source++) {
+ *dest = (char)ACPI_TOUPPER(*source);
+ }
+
+ acpi_ut_remove_reference(return_object);
+ *return_object_ptr = new_string;
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
* FUNCTION: acpi_ns_repair_TSS
*
* PARAMETERS: Data - Pointer to validation data structure
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index e1add3491b04..a7d6ad9c111b 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/drivers/acpi/acpica/nsutils.c
@@ -60,104 +60,6 @@ acpi_name acpi_ns_find_parent_name(struct acpi_namespace_node *node_to_search);
/*******************************************************************************
*
- * FUNCTION: acpi_ns_report_error
- *
- * PARAMETERS: module_name - Caller's module name (for error output)
- * line_number - Caller's line number (for error output)
- * internal_name - Name or path of the namespace node
- * lookup_status - Exception code from NS lookup
- *
- * RETURN: None
- *
- * DESCRIPTION: Print warning message with full pathname
- *
- ******************************************************************************/
-
-void
-acpi_ns_report_error(const char *module_name,
- u32 line_number,
- const char *internal_name, acpi_status lookup_status)
-{
- acpi_status status;
- u32 bad_name;
- char *name = NULL;
-
- acpi_os_printf("ACPI Error (%s-%04d): ", module_name, line_number);
-
- if (lookup_status == AE_BAD_CHARACTER) {
-
- /* There is a non-ascii character in the name */
-
- ACPI_MOVE_32_TO_32(&bad_name,
- ACPI_CAST_PTR(u32, internal_name));
- acpi_os_printf("[0x%4.4X] (NON-ASCII)", bad_name);
- } else {
- /* Convert path to external format */
-
- status = acpi_ns_externalize_name(ACPI_UINT32_MAX,
- internal_name, NULL, &name);
-
- /* Print target name */
-
- if (ACPI_SUCCESS(status)) {
- acpi_os_printf("[%s]", name);
- } else {
- acpi_os_printf("[COULD NOT EXTERNALIZE NAME]");
- }
-
- if (name) {
- ACPI_FREE(name);
- }
- }
-
- acpi_os_printf(" Namespace lookup failure, %s\n",
- acpi_format_exception(lookup_status));
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ns_report_method_error
- *
- * PARAMETERS: module_name - Caller's module name (for error output)
- * line_number - Caller's line number (for error output)
- * Message - Error message to use on failure
- * prefix_node - Prefix relative to the path
- * Path - Path to the node (optional)
- * method_status - Execution status
- *
- * RETURN: None
- *
- * DESCRIPTION: Print warning message with full pathname
- *
- ******************************************************************************/
-
-void
-acpi_ns_report_method_error(const char *module_name,
- u32 line_number,
- const char *message,
- struct acpi_namespace_node *prefix_node,
- const char *path, acpi_status method_status)
-{
- acpi_status status;
- struct acpi_namespace_node *node = prefix_node;
-
- acpi_os_printf("ACPI Error (%s-%04d): ", module_name, line_number);
-
- if (path) {
- status =
- acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH,
- &node);
- if (ACPI_FAILURE(status)) {
- acpi_os_printf("[Could not get node by pathname]");
- }
- }
-
- acpi_ns_print_node_pathname(node, message);
- acpi_os_printf(", %s\n", acpi_format_exception(method_status));
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_ns_print_node_pathname
*
* PARAMETERS: Node - Object
diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c
index 1728cb9bf600..d2ff4325c427 100644
--- a/drivers/acpi/acpica/tbfadt.c
+++ b/drivers/acpi/acpica/tbfadt.c
@@ -49,7 +49,7 @@
ACPI_MODULE_NAME("tbfadt")
/* Local prototypes */
-static inline void
+static ACPI_INLINE void
acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
u8 space_id, u8 byte_width, u64 address);
@@ -181,7 +181,7 @@ static struct acpi_fadt_pm_info fadt_pm_info_table[] = {
*
******************************************************************************/
-static inline void
+static ACPI_INLINE void
acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
u8 space_id, u8 byte_width, u64 address)
{
diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c
index 983510640059..f21c486929a5 100644
--- a/drivers/acpi/acpica/utdebug.c
+++ b/drivers/acpi/acpica/utdebug.c
@@ -179,9 +179,8 @@ acpi_debug_print(u32 requested_debug_level,
if (thread_id != acpi_gbl_prev_thread_id) {
if (ACPI_LV_THREADS & acpi_dbg_level) {
acpi_os_printf
- ("\n**** Context Switch from TID %p to TID %p ****\n\n",
- ACPI_CAST_PTR(void, acpi_gbl_prev_thread_id),
- ACPI_CAST_PTR(void, thread_id));
+ ("\n**** Context Switch from TID %u to TID %u ****\n\n",
+ (u32)acpi_gbl_prev_thread_id, (u32)thread_id);
}
acpi_gbl_prev_thread_id = thread_id;
@@ -194,7 +193,7 @@ acpi_debug_print(u32 requested_debug_level,
acpi_os_printf("%8s-%04ld ", module_name, line_number);
if (ACPI_LV_THREADS & acpi_dbg_level) {
- acpi_os_printf("[%p] ", ACPI_CAST_PTR(void, thread_id));
+ acpi_os_printf("[%u] ", (u32)thread_id);
}
acpi_os_printf("[%02ld] %-22.22s: ",
diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c
index 6dfdeb653490..22f59ef604e0 100644
--- a/drivers/acpi/acpica/uteval.c
+++ b/drivers/acpi/acpica/uteval.c
@@ -48,153 +48,6 @@
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME("uteval")
-/*
- * Strings supported by the _OSI predefined (internal) method.
- *
- * March 2009: Removed "Linux" as this host no longer wants to respond true
- * for this string. Basically, the only safe OS strings are windows-related
- * and in many or most cases represent the only test path within the
- * BIOS-provided ASL code.
- *
- * The second element of each entry is used to track the newest version of
- * Windows that the BIOS has requested.
- */
-static struct acpi_interface_info acpi_interfaces_supported[] = {
- /* Operating System Vendor Strings */
-
- {"Windows 2000", ACPI_OSI_WIN_2000}, /* Windows 2000 */
- {"Windows 2001", ACPI_OSI_WIN_XP}, /* Windows XP */
- {"Windows 2001 SP1", ACPI_OSI_WIN_XP_SP1}, /* Windows XP SP1 */
- {"Windows 2001.1", ACPI_OSI_WINSRV_2003}, /* Windows Server 2003 */
- {"Windows 2001 SP2", ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */
- {"Windows 2001.1 SP1", ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */
- {"Windows 2006", ACPI_OSI_WIN_VISTA}, /* Windows Vista - Added 03/2006 */
- {"Windows 2006.1", ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */
- {"Windows 2006 SP1", ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */
- {"Windows 2009", ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */
-
- /* Feature Group Strings */
-
- {"Extended Address Space Descriptor", 0}
-
- /*
- * All "optional" feature group strings (features that are implemented
- * by the host) should be implemented in the host version of
- * acpi_os_validate_interface and should not be added here.
- */
-};
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_osi_implementation
- *
- * PARAMETERS: walk_state - Current walk state
- *
- * RETURN: Status
- *
- * DESCRIPTION: Implementation of the _OSI predefined control method
- *
- ******************************************************************************/
-
-acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
-{
- acpi_status status;
- union acpi_operand_object *string_desc;
- union acpi_operand_object *return_desc;
- u32 return_value;
- u32 i;
-
- ACPI_FUNCTION_TRACE(ut_osi_implementation);
-
- /* Validate the string input argument */
-
- string_desc = walk_state->arguments[0].object;
- if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) {
- return_ACPI_STATUS(AE_TYPE);
- }
-
- /* Create a return object */
-
- return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
- if (!return_desc) {
- return_ACPI_STATUS(AE_NO_MEMORY);
- }
-
- /* Default return value is 0, NOT SUPPORTED */
-
- return_value = 0;
-
- /* Compare input string to static table of supported interfaces */
-
- for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_interfaces_supported); i++) {
- if (!ACPI_STRCMP(string_desc->string.pointer,
- acpi_interfaces_supported[i].name)) {
- /*
- * The interface is supported.
- * Update the osi_data if necessary. We keep track of the latest
- * version of Windows that has been requested by the BIOS.
- */
- if (acpi_interfaces_supported[i].value >
- acpi_gbl_osi_data) {
- acpi_gbl_osi_data =
- acpi_interfaces_supported[i].value;
- }
-
- return_value = ACPI_UINT32_MAX;
- goto exit;
- }
- }
-
- /*
- * Did not match the string in the static table, call the host OSL to
- * check for a match with one of the optional strings (such as
- * "Module Device", "3.0 Thermal Model", etc.)
- */
- status = acpi_os_validate_interface(string_desc->string.pointer);
- if (ACPI_SUCCESS(status)) {
-
- /* The interface is supported */
-
- return_value = ACPI_UINT32_MAX;
- }
-
-exit:
- ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO,
- "ACPI: BIOS _OSI(%s) is %ssupported\n",
- string_desc->string.pointer, return_value == 0 ? "not " : ""));
-
- /* Complete the return value */
-
- return_desc->integer.value = return_value;
- walk_state->return_desc = return_desc;
- return_ACPI_STATUS (AE_OK);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_osi_invalidate
- *
- * PARAMETERS: interface_string
- *
- * RETURN: Status
- *
- * DESCRIPTION: invalidate string in pre-defiend _OSI string list
- *
- ******************************************************************************/
-
-acpi_status acpi_osi_invalidate(char *interface)
-{
- int i;
-
- for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_interfaces_supported); i++) {
- if (!ACPI_STRCMP(interface, acpi_interfaces_supported[i].name)) {
- *acpi_interfaces_supported[i].name = '\0';
- return AE_OK;
- }
- }
- return AE_NOT_FOUND;
-}
-
/*******************************************************************************
*
* FUNCTION: acpi_ut_evaluate_object
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
index 0558747579ef..e87bc6760be6 100644
--- a/drivers/acpi/acpica/utglobal.c
+++ b/drivers/acpi/acpica/utglobal.c
@@ -154,14 +154,16 @@ ACPI_EXPORT_SYMBOL(acpi_format_exception)
* 1) _SB_ is defined to be a device to allow \_SB_._INI to be run
* during the initialization sequence.
* 2) _TZ_ is defined to be a thermal zone in order to allow ASL code to
- * perform a Notify() operation on it.
+ * perform a Notify() operation on it. 09/2010: Changed to type Device.
+ * This still allows notifies, but does not confuse host code that
+ * searches for valid thermal_zone objects.
*/
const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = {
{"_GPE", ACPI_TYPE_LOCAL_SCOPE, NULL},
{"_PR_", ACPI_TYPE_LOCAL_SCOPE, NULL},
{"_SB_", ACPI_TYPE_DEVICE, NULL},
{"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL},
- {"_TZ_", ACPI_TYPE_THERMAL, NULL},
+ {"_TZ_", ACPI_TYPE_DEVICE, NULL},
{"_REV", ACPI_TYPE_INTEGER, (char *)ACPI_CA_SUPPORT_LEVEL},
{"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME},
{"_GL_", ACPI_TYPE_MUTEX, (char *)1},
@@ -766,6 +768,7 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_gpe_fadt_blocks[0] = NULL;
acpi_gbl_gpe_fadt_blocks[1] = NULL;
acpi_current_gpe_count = 0;
+ acpi_all_gpes_initialized = FALSE;
/* Global handlers */
@@ -774,6 +777,7 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_exception_handler = NULL;
acpi_gbl_init_handler = NULL;
acpi_gbl_table_handler = NULL;
+ acpi_gbl_interface_handler = NULL;
/* Global Lock support */
@@ -800,6 +804,7 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_debugger_configuration = DEBUGGER_THREADING;
acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT;
acpi_gbl_osi_data = 0;
+ acpi_gbl_osi_mutex = NULL;
/* Hardware oriented */
diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c
index 1397fadd0d4b..d2906328535d 100644
--- a/drivers/acpi/acpica/utids.c
+++ b/drivers/acpi/acpica/utids.c
@@ -48,42 +48,6 @@
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME("utids")
-/* Local prototypes */
-static void acpi_ut_copy_id_string(char *destination, char *source);
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_copy_id_string
- *
- * PARAMETERS: Destination - Where to copy the string
- * Source - Source string
- *
- * RETURN: None
- *
- * DESCRIPTION: Copies an ID string for the _HID, _CID, and _UID methods.
- * Performs removal of a leading asterisk if present -- workaround
- * for a known issue on a bunch of machines.
- *
- ******************************************************************************/
-
-static void acpi_ut_copy_id_string(char *destination, char *source)
-{
-
- /*
- * Workaround for ID strings that have a leading asterisk. This construct
- * is not allowed by the ACPI specification (ID strings must be
- * alphanumeric), but enough existing machines have this embedded in their
- * ID strings that the following code is useful.
- */
- if (*source == '*') {
- source++;
- }
-
- /* Do the actual copy */
-
- ACPI_STRCPY(destination, source);
-}
-
/*******************************************************************************
*
* FUNCTION: acpi_ut_execute_HID
@@ -101,7 +65,6 @@ static void acpi_ut_copy_id_string(char *destination, char *source)
* NOTE: Internal function, no parameter validation
*
******************************************************************************/
-
acpi_status
acpi_ut_execute_HID(struct acpi_namespace_node *device_node,
struct acpica_device_id **return_id)
@@ -147,7 +110,7 @@ acpi_ut_execute_HID(struct acpi_namespace_node *device_node,
if (obj_desc->common.type == ACPI_TYPE_INTEGER) {
acpi_ex_eisa_id_to_string(hid->string, obj_desc->integer.value);
} else {
- acpi_ut_copy_id_string(hid->string, obj_desc->string.pointer);
+ ACPI_STRCPY(hid->string, obj_desc->string.pointer);
}
hid->length = length;
@@ -224,7 +187,7 @@ acpi_ut_execute_UID(struct acpi_namespace_node *device_node,
if (obj_desc->common.type == ACPI_TYPE_INTEGER) {
acpi_ex_integer_to_string(uid->string, obj_desc->integer.value);
} else {
- acpi_ut_copy_id_string(uid->string, obj_desc->string.pointer);
+ ACPI_STRCPY(uid->string, obj_desc->string.pointer);
}
uid->length = length;
@@ -357,8 +320,8 @@ acpi_ut_execute_CID(struct acpi_namespace_node *device_node,
/* Copy the String CID from the returned object */
- acpi_ut_copy_id_string(next_id_string,
- cid_objects[i]->string.pointer);
+ ACPI_STRCPY(next_id_string,
+ cid_objects[i]->string.pointer);
length = cid_objects[i]->string.length + 1;
}
diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c
index a39c93dac719..c1b1c803ea9b 100644
--- a/drivers/acpi/acpica/utinit.c
+++ b/drivers/acpi/acpica/utinit.c
@@ -117,6 +117,10 @@ void acpi_ut_subsystem_shutdown(void)
/* Close the acpi_event Handling */
acpi_ev_terminate();
+
+ /* Delete any dynamic _OSI interfaces */
+
+ acpi_ut_interface_terminate();
#endif
/* Close the Namespace */
diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c
index 35059a14eb72..49cf7b7fd816 100644
--- a/drivers/acpi/acpica/utmath.c
+++ b/drivers/acpi/acpica/utmath.c
@@ -48,11 +48,27 @@
ACPI_MODULE_NAME("utmath")
/*
- * Support for double-precision integer divide. This code is included here
- * in order to support kernel environments where the double-precision math
- * library is not available.
+ * Optional support for 64-bit double-precision integer divide. This code
+ * is configurable and is implemented in order to support 32-bit kernel
+ * environments where a 64-bit double-precision math library is not available.
+ *
+ * Support for a more normal 64-bit divide/modulo (with check for a divide-
+ * by-zero) appears after this optional section of code.
*/
#ifndef ACPI_USE_NATIVE_DIVIDE
+/* Structures used only for 64-bit divide */
+typedef struct uint64_struct {
+ u32 lo;
+ u32 hi;
+
+} uint64_struct;
+
+typedef union uint64_overlay {
+ u64 full;
+ struct uint64_struct part;
+
+} uint64_overlay;
+
/*******************************************************************************
*
* FUNCTION: acpi_ut_short_divide
@@ -69,6 +85,7 @@ ACPI_MODULE_NAME("utmath")
* 32-bit remainder.
*
******************************************************************************/
+
acpi_status
acpi_ut_short_divide(u64 dividend,
u32 divisor, u64 *out_quotient, u32 *out_remainder)
diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c
index e8d0724ee403..c7d0e05ef5a4 100644
--- a/drivers/acpi/acpica/utmisc.c
+++ b/drivers/acpi/acpica/utmisc.c
@@ -50,11 +50,6 @@
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME("utmisc")
-/*
- * Common suffix for messages
- */
-#define ACPI_COMMON_MSG_SUFFIX \
- acpi_os_printf(" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number)
/*******************************************************************************
*
* FUNCTION: acpi_ut_validate_exception
@@ -1044,160 +1039,3 @@ acpi_ut_walk_package_tree(union acpi_operand_object * source_object,
return_ACPI_STATUS(AE_AML_INTERNAL);
}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_error, acpi_exception, acpi_warning, acpi_info
- *
- * PARAMETERS: module_name - Caller's module name (for error output)
- * line_number - Caller's line number (for error output)
- * Format - Printf format string + additional args
- *
- * RETURN: None
- *
- * DESCRIPTION: Print message with module/line/version info
- *
- ******************************************************************************/
-
-void ACPI_INTERNAL_VAR_XFACE
-acpi_error(const char *module_name, u32 line_number, const char *format, ...)
-{
- va_list args;
-
- acpi_os_printf("ACPI Error: ");
-
- va_start(args, format);
- acpi_os_vprintf(format, args);
- ACPI_COMMON_MSG_SUFFIX;
- va_end(args);
-}
-
-void ACPI_INTERNAL_VAR_XFACE
-acpi_exception(const char *module_name,
- u32 line_number, acpi_status status, const char *format, ...)
-{
- va_list args;
-
- acpi_os_printf("ACPI Exception: %s, ", acpi_format_exception(status));
-
- va_start(args, format);
- acpi_os_vprintf(format, args);
- ACPI_COMMON_MSG_SUFFIX;
- va_end(args);
-}
-
-void ACPI_INTERNAL_VAR_XFACE
-acpi_warning(const char *module_name, u32 line_number, const char *format, ...)
-{
- va_list args;
-
- acpi_os_printf("ACPI Warning: ");
-
- va_start(args, format);
- acpi_os_vprintf(format, args);
- ACPI_COMMON_MSG_SUFFIX;
- va_end(args);
-}
-
-void ACPI_INTERNAL_VAR_XFACE
-acpi_info(const char *module_name, u32 line_number, const char *format, ...)
-{
- va_list args;
-
- acpi_os_printf("ACPI: ");
-
- va_start(args, format);
- acpi_os_vprintf(format, args);
- acpi_os_printf("\n");
- va_end(args);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_error)
-ACPI_EXPORT_SYMBOL(acpi_exception)
-ACPI_EXPORT_SYMBOL(acpi_warning)
-ACPI_EXPORT_SYMBOL(acpi_info)
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_predefined_warning
- *
- * PARAMETERS: module_name - Caller's module name (for error output)
- * line_number - Caller's line number (for error output)
- * Pathname - Full pathname to the node
- * node_flags - From Namespace node for the method/object
- * Format - Printf format string + additional args
- *
- * RETURN: None
- *
- * DESCRIPTION: Warnings for the predefined validation module. Messages are
- * only emitted the first time a problem with a particular
- * method/object is detected. This prevents a flood of error
- * messages for methods that are repeatedly evaluated.
- *
-******************************************************************************/
-
-void ACPI_INTERNAL_VAR_XFACE
-acpi_ut_predefined_warning(const char *module_name,
- u32 line_number,
- char *pathname,
- u8 node_flags, const char *format, ...)
-{
- va_list args;
-
- /*
- * Warning messages for this method/object will be disabled after the
- * first time a validation fails or an object is successfully repaired.
- */
- if (node_flags & ANOBJ_EVALUATED) {
- return;
- }
-
- acpi_os_printf("ACPI Warning for %s: ", pathname);
-
- va_start(args, format);
- acpi_os_vprintf(format, args);
- ACPI_COMMON_MSG_SUFFIX;
- va_end(args);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_predefined_info
- *
- * PARAMETERS: module_name - Caller's module name (for error output)
- * line_number - Caller's line number (for error output)
- * Pathname - Full pathname to the node
- * node_flags - From Namespace node for the method/object
- * Format - Printf format string + additional args
- *
- * RETURN: None
- *
- * DESCRIPTION: Info messages for the predefined validation module. Messages
- * are only emitted the first time a problem with a particular
- * method/object is detected. This prevents a flood of
- * messages for methods that are repeatedly evaluated.
- *
- ******************************************************************************/
-
-void ACPI_INTERNAL_VAR_XFACE
-acpi_ut_predefined_info(const char *module_name,
- u32 line_number,
- char *pathname, u8 node_flags, const char *format, ...)
-{
- va_list args;
-
- /*
- * Warning messages for this method/object will be disabled after the
- * first time a validation fails or an object is successfully repaired.
- */
- if (node_flags & ANOBJ_EVALUATED) {
- return;
- }
-
- acpi_os_printf("ACPI Info for %s: ", pathname);
-
- va_start(args, format);
- acpi_os_vprintf(format, args);
- ACPI_COMMON_MSG_SUFFIX;
- va_end(args);
-}
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index f5cca3a1300c..d9efa495b433 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -86,6 +86,12 @@ acpi_status acpi_ut_mutex_initialize(void)
spin_lock_init(acpi_gbl_gpe_lock);
spin_lock_init(acpi_gbl_hardware_lock);
+ /* Mutex for _OSI support */
+ status = acpi_os_create_mutex(&acpi_gbl_osi_mutex);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
/* Create the reader/writer lock for namespace access */
status = acpi_ut_create_rw_lock(&acpi_gbl_namespace_rw_lock);
@@ -117,6 +123,8 @@ void acpi_ut_mutex_terminate(void)
acpi_ut_delete_mutex(i);
}
+ acpi_os_delete_mutex(acpi_gbl_osi_mutex);
+
/* Delete the spinlocks */
acpi_os_delete_lock(acpi_gbl_gpe_lock);
@@ -220,18 +228,17 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id)
if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) {
if (i == mutex_id) {
ACPI_ERROR((AE_INFO,
- "Mutex [%s] already acquired by this thread [%p]",
+ "Mutex [%s] already acquired by this thread [%u]",
acpi_ut_get_mutex_name
(mutex_id),
- ACPI_CAST_PTR(void,
- this_thread_id)));
+ (u32)this_thread_id));
return (AE_ALREADY_ACQUIRED);
}
ACPI_ERROR((AE_INFO,
- "Invalid acquire order: Thread %p owns [%s], wants [%s]",
- ACPI_CAST_PTR(void, this_thread_id),
+ "Invalid acquire order: Thread %u owns [%s], wants [%s]",
+ (u32)this_thread_id,
acpi_ut_get_mutex_name(i),
acpi_ut_get_mutex_name(mutex_id)));
@@ -242,24 +249,24 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id)
#endif
ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
- "Thread %p attempting to acquire Mutex [%s]\n",
- ACPI_CAST_PTR(void, this_thread_id),
+ "Thread %u attempting to acquire Mutex [%s]\n",
+ (u32)this_thread_id,
acpi_ut_get_mutex_name(mutex_id)));
status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex,
ACPI_WAIT_FOREVER);
if (ACPI_SUCCESS(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
- "Thread %p acquired Mutex [%s]\n",
- ACPI_CAST_PTR(void, this_thread_id),
+ "Thread %u acquired Mutex [%s]\n",
+ (u32)this_thread_id,
acpi_ut_get_mutex_name(mutex_id)));
acpi_gbl_mutex_info[mutex_id].use_count++;
acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id;
} else {
ACPI_EXCEPTION((AE_INFO, status,
- "Thread %p could not acquire Mutex [0x%X]",
- ACPI_CAST_PTR(void, this_thread_id), mutex_id));
+ "Thread %u could not acquire Mutex [0x%X]",
+ (u32)this_thread_id, mutex_id));
}
return (status);
@@ -279,10 +286,14 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id)
acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id)
{
+ acpi_thread_id this_thread_id;
+
ACPI_FUNCTION_NAME(ut_release_mutex);
- ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %p releasing Mutex [%s]\n",
- ACPI_CAST_PTR(void, acpi_os_get_thread_id()),
+ this_thread_id = acpi_os_get_thread_id();
+
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %u releasing Mutex [%s]\n",
+ (u32)this_thread_id,
acpi_ut_get_mutex_name(mutex_id)));
if (mutex_id > ACPI_MAX_MUTEX) {
diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c
new file mode 100644
index 000000000000..18c59a85fdca
--- /dev/null
+++ b/drivers/acpi/acpica/utosi.c
@@ -0,0 +1,380 @@
+/******************************************************************************
+ *
+ * Module Name: utosi - Support for the _OSI predefined control method
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2010, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utosi")
+
+/*
+ * Strings supported by the _OSI predefined control method (which is
+ * implemented internally within this module.)
+ *
+ * March 2009: Removed "Linux" as this host no longer wants to respond true
+ * for this string. Basically, the only safe OS strings are windows-related
+ * and in many or most cases represent the only test path within the
+ * BIOS-provided ASL code.
+ *
+ * The last element of each entry is used to track the newest version of
+ * Windows that the BIOS has requested.
+ */
+static struct acpi_interface_info acpi_default_supported_interfaces[] = {
+ /* Operating System Vendor Strings */
+
+ {"Windows 2000", NULL, 0, ACPI_OSI_WIN_2000}, /* Windows 2000 */
+ {"Windows 2001", NULL, 0, ACPI_OSI_WIN_XP}, /* Windows XP */
+ {"Windows 2001 SP1", NULL, 0, ACPI_OSI_WIN_XP_SP1}, /* Windows XP SP1 */
+ {"Windows 2001.1", NULL, 0, ACPI_OSI_WINSRV_2003}, /* Windows Server 2003 */
+ {"Windows 2001 SP2", NULL, 0, ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */
+ {"Windows 2001.1 SP1", NULL, 0, ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */
+ {"Windows 2006", NULL, 0, ACPI_OSI_WIN_VISTA}, /* Windows Vista - Added 03/2006 */
+ {"Windows 2006.1", NULL, 0, ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */
+ {"Windows 2006 SP1", NULL, 0, ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */
+ {"Windows 2006 SP2", NULL, 0, ACPI_OSI_WIN_VISTA_SP2}, /* Windows Vista SP2 - Added 09/2010 */
+ {"Windows 2009", NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */
+
+ /* Feature Group Strings */
+
+ {"Extended Address Space Descriptor", NULL, 0, 0}
+
+ /*
+ * All "optional" feature group strings (features that are implemented
+ * by the host) should be dynamically added by the host via
+ * acpi_install_interface and should not be manually added here.
+ *
+ * Examples of optional feature group strings:
+ *
+ * "Module Device"
+ * "Processor Device"
+ * "3.0 Thermal Model"
+ * "3.0 _SCP Extensions"
+ * "Processor Aggregator Device"
+ */
+};
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_initialize_interfaces
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize the global _OSI supported interfaces list
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_initialize_interfaces(void)
+{
+ u32 i;
+
+ (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ acpi_gbl_supported_interfaces = acpi_default_supported_interfaces;
+
+ /* Link the static list of supported interfaces */
+
+ for (i = 0;
+ i < (ACPI_ARRAY_LENGTH(acpi_default_supported_interfaces) - 1);
+ i++) {
+ acpi_default_supported_interfaces[i].next =
+ &acpi_default_supported_interfaces[(acpi_size) i + 1];
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_interface_terminate
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete all interfaces in the global list. Sets
+ * acpi_gbl_supported_interfaces to NULL.
+ *
+ ******************************************************************************/
+
+void acpi_ut_interface_terminate(void)
+{
+ struct acpi_interface_info *next_interface;
+
+ (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+ next_interface = acpi_gbl_supported_interfaces;
+
+ while (next_interface) {
+ acpi_gbl_supported_interfaces = next_interface->next;
+
+ /* Only interfaces added at runtime can be freed */
+
+ if (next_interface->flags & ACPI_OSI_DYNAMIC) {
+ ACPI_FREE(next_interface->name);
+ ACPI_FREE(next_interface);
+ }
+
+ next_interface = acpi_gbl_supported_interfaces;
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_install_interface
+ *
+ * PARAMETERS: interface_name - The interface to install
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install the interface into the global interface list.
+ * Caller MUST hold acpi_gbl_osi_mutex
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_install_interface(acpi_string interface_name)
+{
+ struct acpi_interface_info *interface_info;
+
+ /* Allocate info block and space for the name string */
+
+ interface_info =
+ ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_interface_info));
+ if (!interface_info) {
+ return (AE_NO_MEMORY);
+ }
+
+ interface_info->name =
+ ACPI_ALLOCATE_ZEROED(ACPI_STRLEN(interface_name) + 1);
+ if (!interface_info->name) {
+ ACPI_FREE(interface_info);
+ return (AE_NO_MEMORY);
+ }
+
+ /* Initialize new info and insert at the head of the global list */
+
+ ACPI_STRCPY(interface_info->name, interface_name);
+ interface_info->flags = ACPI_OSI_DYNAMIC;
+ interface_info->next = acpi_gbl_supported_interfaces;
+
+ acpi_gbl_supported_interfaces = interface_info;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_remove_interface
+ *
+ * PARAMETERS: interface_name - The interface to remove
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove the interface from the global interface list.
+ * Caller MUST hold acpi_gbl_osi_mutex
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_remove_interface(acpi_string interface_name)
+{
+ struct acpi_interface_info *previous_interface;
+ struct acpi_interface_info *next_interface;
+
+ previous_interface = next_interface = acpi_gbl_supported_interfaces;
+ while (next_interface) {
+ if (!ACPI_STRCMP(interface_name, next_interface->name)) {
+
+ /* Found: name is in either the static list or was added at runtime */
+
+ if (next_interface->flags & ACPI_OSI_DYNAMIC) {
+
+ /* Interface was added dynamically, remove and free it */
+
+ if (previous_interface == next_interface) {
+ acpi_gbl_supported_interfaces =
+ next_interface->next;
+ } else {
+ previous_interface->next =
+ next_interface->next;
+ }
+
+ ACPI_FREE(next_interface->name);
+ ACPI_FREE(next_interface);
+ } else {
+ /*
+ * Interface is in static list. If marked invalid, then it
+ * does not actually exist. Else, mark it invalid.
+ */
+ if (next_interface->flags & ACPI_OSI_INVALID) {
+ return (AE_NOT_EXIST);
+ }
+
+ next_interface->flags |= ACPI_OSI_INVALID;
+ }
+
+ return (AE_OK);
+ }
+
+ previous_interface = next_interface;
+ next_interface = next_interface->next;
+ }
+
+ /* Interface was not found */
+
+ return (AE_NOT_EXIST);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_get_interface
+ *
+ * PARAMETERS: interface_name - The interface to find
+ *
+ * RETURN: struct acpi_interface_info if found. NULL if not found.
+ *
+ * DESCRIPTION: Search for the specified interface name in the global list.
+ * Caller MUST hold acpi_gbl_osi_mutex
+ *
+ ******************************************************************************/
+
+struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name)
+{
+ struct acpi_interface_info *next_interface;
+
+ next_interface = acpi_gbl_supported_interfaces;
+ while (next_interface) {
+ if (!ACPI_STRCMP(interface_name, next_interface->name)) {
+ return (next_interface);
+ }
+
+ next_interface = next_interface->next;
+ }
+
+ return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_osi_implementation
+ *
+ * PARAMETERS: walk_state - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Implementation of the _OSI predefined control method. When
+ * an invocation of _OSI is encountered in the system AML,
+ * control is transferred to this function.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_osi_implementation(struct acpi_walk_state * walk_state)
+{
+ union acpi_operand_object *string_desc;
+ union acpi_operand_object *return_desc;
+ struct acpi_interface_info *interface_info;
+ acpi_interface_handler interface_handler;
+ u32 return_value;
+
+ ACPI_FUNCTION_TRACE(ut_osi_implementation);
+
+ /* Validate the string input argument (from the AML caller) */
+
+ string_desc = walk_state->arguments[0].object;
+ if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) {
+ return_ACPI_STATUS(AE_TYPE);
+ }
+
+ /* Create a return object */
+
+ return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!return_desc) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Default return value is 0, NOT SUPPORTED */
+
+ return_value = 0;
+ (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+
+ /* Lookup the interface in the global _OSI list */
+
+ interface_info = acpi_ut_get_interface(string_desc->string.pointer);
+ if (interface_info && !(interface_info->flags & ACPI_OSI_INVALID)) {
+ /*
+ * The interface is supported.
+ * Update the osi_data if necessary. We keep track of the latest
+ * version of Windows that has been requested by the BIOS.
+ */
+ if (interface_info->value > acpi_gbl_osi_data) {
+ acpi_gbl_osi_data = interface_info->value;
+ }
+
+ return_value = ACPI_UINT32_MAX;
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+
+ /*
+ * Invoke an optional _OSI interface handler. The host OS may wish
+ * to do some interface-specific handling. For example, warn about
+ * certain interfaces or override the true/false support value.
+ */
+ interface_handler = acpi_gbl_interface_handler;
+ if (interface_handler) {
+ return_value =
+ interface_handler(string_desc->string.pointer,
+ return_value);
+ }
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
+ "ACPI: BIOS _OSI(\"%s\") is %ssupported\n",
+ string_desc->string.pointer,
+ return_value == 0 ? "not " : ""));
+
+ /* Complete the return object */
+
+ return_desc->integer.value = return_value;
+ walk_state->return_desc = return_desc;
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
index 7f8cefcb2b32..1f484c9a6888 100644
--- a/drivers/acpi/acpica/utxface.c
+++ b/drivers/acpi/acpica/utxface.c
@@ -110,6 +110,15 @@ acpi_status __init acpi_initialize_subsystem(void)
return_ACPI_STATUS(status);
}
+ /* Initialize the global OSI interfaces list with the static names */
+
+ status = acpi_ut_initialize_interfaces();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During OSI interfaces initialization"));
+ return_ACPI_STATUS(status);
+ }
+
/* If configured, initialize the AML debugger */
ACPI_DEBUGGER_EXEC(status = acpi_db_initialize());
@@ -290,19 +299,6 @@ acpi_status acpi_initialize_objects(u32 flags)
}
/*
- * Complete the GPE initialization for the GPE blocks defined in the FADT
- * (GPE block 0 and 1).
- *
- * NOTE: Currently, there seems to be no need to run the _REG methods
- * before enabling the GPEs.
- */
- if (!(flags & ACPI_NO_EVENT_INIT)) {
- status = acpi_ev_install_fadt_gpes();
- if (ACPI_FAILURE(status))
- return (status);
- }
-
- /*
* Empty the caches (delete the cached objects) on the assumption that
* the table load filled them up more than they will be at runtime --
* thus wasting non-paged memory.
@@ -506,6 +502,7 @@ acpi_install_initialization_handler(acpi_init_handler handler, u32 function)
ACPI_EXPORT_SYMBOL(acpi_install_initialization_handler)
#endif /* ACPI_FUTURE_USAGE */
+
/*****************************************************************************
*
* FUNCTION: acpi_purge_cached_objects
@@ -529,4 +526,117 @@ acpi_status acpi_purge_cached_objects(void)
}
ACPI_EXPORT_SYMBOL(acpi_purge_cached_objects)
-#endif
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_install_interface
+ *
+ * PARAMETERS: interface_name - The interface to install
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install an _OSI interface to the global list
+ *
+ ****************************************************************************/
+acpi_status acpi_install_interface(acpi_string interface_name)
+{
+ acpi_status status;
+ struct acpi_interface_info *interface_info;
+
+ /* Parameter validation */
+
+ if (!interface_name || (ACPI_STRLEN(interface_name) == 0)) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+
+ /* Check if the interface name is already in the global list */
+
+ interface_info = acpi_ut_get_interface(interface_name);
+ if (interface_info) {
+ /*
+ * The interface already exists in the list. This is OK if the
+ * interface has been marked invalid -- just clear the bit.
+ */
+ if (interface_info->flags & ACPI_OSI_INVALID) {
+ interface_info->flags &= ~ACPI_OSI_INVALID;
+ status = AE_OK;
+ } else {
+ status = AE_ALREADY_EXISTS;
+ }
+ } else {
+ /* New interface name, install into the global list */
+
+ status = acpi_ut_install_interface(interface_name);
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_interface)
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_remove_interface
+ *
+ * PARAMETERS: interface_name - The interface to remove
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove an _OSI interface from the global list
+ *
+ ****************************************************************************/
+acpi_status acpi_remove_interface(acpi_string interface_name)
+{
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!interface_name || (ACPI_STRLEN(interface_name) == 0)) {
+ return (AE_BAD_PARAMETER);
+ }
+
+ (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+
+ status = acpi_ut_remove_interface(interface_name);
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_interface)
+
+/*****************************************************************************
+ *
+ * FUNCTION: acpi_install_interface_handler
+ *
+ * PARAMETERS: Handler - The _OSI interface handler to install
+ * NULL means "remove existing handler"
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for the predefined _OSI ACPI method.
+ * invoked during execution of the internal implementation of
+ * _OSI. A NULL handler simply removes any existing handler.
+ *
+ ****************************************************************************/
+acpi_status acpi_install_interface_handler(acpi_interface_handler handler)
+{
+ acpi_status status = AE_OK;
+
+ (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
+
+ if (handler && acpi_gbl_interface_handler) {
+ status = AE_ALREADY_EXISTS;
+ } else {
+ acpi_gbl_interface_handler = handler;
+ }
+
+ acpi_os_release_mutex(acpi_gbl_osi_mutex);
+ return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_interface_handler)
+#endif /* !ACPI_ASL_COMPILER */
diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c
new file mode 100644
index 000000000000..6f12e314fbae
--- /dev/null
+++ b/drivers/acpi/acpica/utxferror.c
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ *
+ * Module Name: utxferror - Various error/warning output functions
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2010, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utxferror")
+
+/*
+ * This module is used for the in-kernel ACPICA as well as the ACPICA
+ * tools/applications.
+ *
+ * For the i_aSL compiler case, the output is redirected to stderr so that
+ * any of the various ACPI errors and warnings do not appear in the output
+ * files, for either the compiler or disassembler portions of the tool.
+ */
+#ifdef ACPI_ASL_COMPILER
+#include <stdio.h>
+extern FILE *acpi_gbl_output_file;
+
+#define ACPI_MSG_REDIRECT_BEGIN \
+ FILE *output_file = acpi_gbl_output_file; \
+ acpi_os_redirect_output (stderr);
+
+#define ACPI_MSG_REDIRECT_END \
+ acpi_os_redirect_output (output_file);
+
+#else
+/*
+ * non-i_aSL case - no redirection, nothing to do
+ */
+#define ACPI_MSG_REDIRECT_BEGIN
+#define ACPI_MSG_REDIRECT_END
+#endif
+/*
+ * Common message prefixes
+ */
+#define ACPI_MSG_ERROR "ACPI Error: "
+#define ACPI_MSG_EXCEPTION "ACPI Exception: "
+#define ACPI_MSG_WARNING "ACPI Warning: "
+#define ACPI_MSG_INFO "ACPI: "
+/*
+ * Common message suffix
+ */
+#define ACPI_MSG_SUFFIX \
+ acpi_os_printf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print "ACPI Error" message with module/line/version info
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_error(const char *module_name, u32 line_number, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_ERROR);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_error)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_exception
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Status - Status to be formatted
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print "ACPI Exception" message with module/line/version info
+ * and decoded acpi_status.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_exception(const char *module_name,
+ u32 line_number, acpi_status status, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ",
+ acpi_format_exception(status));
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_exception)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_warning
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print "ACPI Warning" message with module/line/version info
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_warning(const char *module_name, u32 line_number, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_WARNING);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_warning)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_info
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print generic "ACPI:" information message. There is no
+ * module/line/version info in order to keep the message simple.
+ *
+ * TBD: module_name and line_number args are not needed, should be removed.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_info(const char *module_name, u32 line_number, const char *format, ...)
+{
+ va_list arg_list;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_INFO);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ acpi_os_printf("\n");
+ va_end(arg_list);
+
+ ACPI_MSG_REDIRECT_END;
+}
+
+ACPI_EXPORT_SYMBOL(acpi_info)
+
+/*
+ * The remainder of this module contains internal error functions that may
+ * be configured out.
+ */
+#if !defined (ACPI_NO_ERROR_MESSAGES) && !defined (ACPI_BIN_APP)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_predefined_warning
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Pathname - Full pathname to the node
+ * node_flags - From Namespace node for the method/object
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Warnings for the predefined validation module. Messages are
+ * only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of error
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_warning(const char *module_name,
+ u32 line_number,
+ char *pathname,
+ u8 node_flags, const char *format, ...)
+{
+ va_list arg_list;
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (node_flags & ANOBJ_EVALUATED) {
+ return;
+ }
+
+ acpi_os_printf(ACPI_MSG_WARNING "For %s: ", pathname);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_predefined_info
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Pathname - Full pathname to the node
+ * node_flags - From Namespace node for the method/object
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Info messages for the predefined validation module. Messages
+ * are only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+ u32 line_number,
+ char *pathname, u8 node_flags, const char *format, ...)
+{
+ va_list arg_list;
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (node_flags & ANOBJ_EVALUATED) {
+ return;
+ }
+
+ acpi_os_printf(ACPI_MSG_INFO "For %s: ", pathname);
+
+ va_start(arg_list, format);
+ acpi_os_vprintf(format, arg_list);
+ ACPI_MSG_SUFFIX;
+ va_end(arg_list);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_namespace_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * internal_name - Name or path of the namespace node
+ * lookup_status - Exception code from NS lookup
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with the full pathname for the NS node.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_namespace_error(const char *module_name,
+ u32 line_number,
+ const char *internal_name, acpi_status lookup_status)
+{
+ acpi_status status;
+ u32 bad_name;
+ char *name = NULL;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_ERROR);
+
+ if (lookup_status == AE_BAD_CHARACTER) {
+
+ /* There is a non-ascii character in the name */
+
+ ACPI_MOVE_32_TO_32(&bad_name,
+ ACPI_CAST_PTR(u32, internal_name));
+ acpi_os_printf("[0x%4.4X] (NON-ASCII)", bad_name);
+ } else {
+ /* Convert path to external format */
+
+ status = acpi_ns_externalize_name(ACPI_UINT32_MAX,
+ internal_name, NULL, &name);
+
+ /* Print target name */
+
+ if (ACPI_SUCCESS(status)) {
+ acpi_os_printf("[%s]", name);
+ } else {
+ acpi_os_printf("[COULD NOT EXTERNALIZE NAME]");
+ }
+
+ if (name) {
+ ACPI_FREE(name);
+ }
+ }
+
+ acpi_os_printf(" Namespace lookup failure, %s",
+ acpi_format_exception(lookup_status));
+
+ ACPI_MSG_SUFFIX;
+ ACPI_MSG_REDIRECT_END;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_method_error
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Message - Error message to use on failure
+ * prefix_node - Prefix relative to the path
+ * Path - Path to the node (optional)
+ * method_status - Execution status
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with the full pathname for the method.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_method_error(const char *module_name,
+ u32 line_number,
+ const char *message,
+ struct acpi_namespace_node *prefix_node,
+ const char *path, acpi_status method_status)
+{
+ acpi_status status;
+ struct acpi_namespace_node *node = prefix_node;
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ acpi_os_printf(ACPI_MSG_ERROR);
+
+ if (path) {
+ status =
+ acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH,
+ &node);
+ if (ACPI_FAILURE(status)) {
+ acpi_os_printf("[Could not get node by pathname]");
+ }
+ }
+
+ acpi_ns_print_node_pathname(node, message);
+ acpi_os_printf(", %s", acpi_format_exception(method_status));
+
+ ACPI_MSG_SUFFIX;
+ ACPI_MSG_REDIRECT_END;
+}
+
+#endif /* ACPI_NO_ERROR_MESSAGES */
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 98417201e9ce..95649d373071 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -42,10 +42,7 @@
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
-
-#ifdef CONFIG_ACPI_SYSFS_POWER
#include <linux/power_supply.h>
-#endif
#define PREFIX "ACPI: "
@@ -98,13 +95,12 @@ enum {
* due to bad math.
*/
ACPI_BATTERY_QUIRK_SIGNED16_CURRENT,
+ ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
};
struct acpi_battery {
struct mutex lock;
-#ifdef CONFIG_ACPI_SYSFS_POWER
struct power_supply bat;
-#endif
struct acpi_device *device;
unsigned long update_time;
int rate_now;
@@ -141,7 +137,6 @@ inline int acpi_battery_present(struct acpi_battery *battery)
return battery->device->status.battery_present;
}
-#ifdef CONFIG_ACPI_SYSFS_POWER
static int acpi_battery_technology(struct acpi_battery *battery)
{
if (!strcasecmp("NiCd", battery->type))
@@ -186,6 +181,7 @@ static int acpi_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
+ int ret = 0;
struct acpi_battery *battery = to_acpi_battery(psy);
if (acpi_battery_present(battery)) {
@@ -214,26 +210,44 @@ static int acpi_battery_get_property(struct power_supply *psy,
val->intval = battery->cycle_count;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
- val->intval = battery->design_voltage * 1000;
+ if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->design_voltage * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = battery->voltage_now * 1000;
+ if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->voltage_now * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_POWER_NOW:
- val->intval = battery->rate_now * 1000;
+ if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->rate_now * 1000;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
- val->intval = battery->design_capacity * 1000;
+ if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->design_capacity * 1000;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_ENERGY_FULL:
- val->intval = battery->full_charge_capacity * 1000;
+ if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->full_charge_capacity * 1000;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
case POWER_SUPPLY_PROP_ENERGY_NOW:
- val->intval = battery->capacity_now * 1000;
+ if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ ret = -ENODEV;
+ else
+ val->intval = battery->capacity_now * 1000;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = battery->model_number;
@@ -245,9 +259,9 @@ static int acpi_battery_get_property(struct power_supply *psy,
val->strval = battery->serial_number;
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ return ret;
}
static enum power_supply_property charge_battery_props[] = {
@@ -281,7 +295,6 @@ static enum power_supply_property energy_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
-#endif
#ifdef CONFIG_ACPI_PROCFS_POWER
inline char *acpi_battery_units(struct acpi_battery *battery)
@@ -412,6 +425,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
result = extract_package(battery, buffer.pointer,
info_offsets, ARRAY_SIZE(info_offsets));
kfree(buffer.pointer);
+ if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
+ battery->full_charge_capacity = battery->design_capacity;
return result;
}
@@ -448,6 +463,10 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
battery->rate_now != -1)
battery->rate_now = abs((s16)battery->rate_now);
+ if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
+ && battery->capacity_now >= 0 && battery->capacity_now <= 100)
+ battery->capacity_now = (battery->capacity_now *
+ battery->full_charge_capacity) / 100;
return result;
}
@@ -492,7 +511,6 @@ static int acpi_battery_init_alarm(struct acpi_battery *battery)
return acpi_battery_set_alarm(battery);
}
-#ifdef CONFIG_ACPI_SYSFS_POWER
static ssize_t acpi_battery_alarm_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -552,7 +570,6 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
power_supply_unregister(&battery->bat);
battery->bat.dev = NULL;
}
-#endif
static void acpi_battery_quirks(struct acpi_battery *battery)
{
@@ -561,6 +578,33 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
}
}
+/*
+ * According to the ACPI spec, some kinds of primary batteries can
+ * report percentage battery remaining capacity directly to OS.
+ * In this case, it reports the Last Full Charged Capacity == 100
+ * and BatteryPresentRate == 0xFFFFFFFF.
+ *
+ * Now we found some battery reports percentage remaining capacity
+ * even if it's rechargeable.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=15979
+ *
+ * Handle this correctly so that they won't break userspace.
+ */
+static void acpi_battery_quirks2(struct acpi_battery *battery)
+{
+ if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
+ return ;
+
+ if (battery->full_charge_capacity == 100 &&
+ battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
+ battery->capacity_now >=0 && battery->capacity_now <= 100) {
+ set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
+ battery->full_charge_capacity = battery->design_capacity;
+ battery->capacity_now = (battery->capacity_now *
+ battery->full_charge_capacity) / 100;
+ }
+}
+
static int acpi_battery_update(struct acpi_battery *battery)
{
int result, old_present = acpi_battery_present(battery);
@@ -568,9 +612,7 @@ static int acpi_battery_update(struct acpi_battery *battery)
if (result)
return result;
if (!acpi_battery_present(battery)) {
-#ifdef CONFIG_ACPI_SYSFS_POWER
sysfs_remove_battery(battery);
-#endif
battery->update_time = 0;
return 0;
}
@@ -582,11 +624,11 @@ static int acpi_battery_update(struct acpi_battery *battery)
acpi_battery_quirks(battery);
acpi_battery_init_alarm(battery);
}
-#ifdef CONFIG_ACPI_SYSFS_POWER
if (!battery->bat.dev)
sysfs_add_battery(battery);
-#endif
- return acpi_battery_get_state(battery);
+ result = acpi_battery_get_state(battery);
+ acpi_battery_quirks2(battery);
+ return result;
}
/* --------------------------------------------------------------------------
@@ -867,26 +909,20 @@ static void acpi_battery_remove_fs(struct acpi_device *device)
static void acpi_battery_notify(struct acpi_device *device, u32 event)
{
struct acpi_battery *battery = acpi_driver_data(device);
-#ifdef CONFIG_ACPI_SYSFS_POWER
struct device *old;
-#endif
if (!battery)
return;
-#ifdef CONFIG_ACPI_SYSFS_POWER
old = battery->bat.dev;
-#endif
acpi_battery_update(battery);
acpi_bus_generate_proc_event(device, event,
acpi_battery_present(battery));
acpi_bus_generate_netlink_event(device->pnp.device_class,
dev_name(&device->dev), event,
acpi_battery_present(battery));
-#ifdef CONFIG_ACPI_SYSFS_POWER
/* acpi_battery_update could remove power_supply object */
if (old && battery->bat.dev)
power_supply_changed(&battery->bat);
-#endif
}
static int acpi_battery_add(struct acpi_device *device)
@@ -934,9 +970,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
#ifdef CONFIG_ACPI_PROCFS_POWER
acpi_battery_remove_fs(device);
#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
sysfs_remove_battery(battery);
-#endif
mutex_destroy(&battery->lock);
kfree(battery);
return 0;
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 310e3b9749cb..d68bd61072bb 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -935,6 +935,12 @@ static int __init acpi_bus_init(void)
goto error1;
}
+ /*
+ * _PDC control method may load dynamic SSDT tables,
+ * and we need to install the table handler before that.
+ */
+ acpi_sysfs_init();
+
acpi_early_processor_set_pdc();
/*
@@ -1026,7 +1032,6 @@ static int __init acpi_init(void)
acpi_scan_init();
acpi_ec_init();
acpi_power_init();
- acpi_sysfs_init();
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 1575a9b51f1d..71ef9cd0735f 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -338,7 +338,8 @@ static int acpi_button_add(struct acpi_device *device)
{
struct acpi_button *button;
struct input_dev *input;
- char *hid, *name, *class;
+ const char *hid = acpi_device_hid(device);
+ char *name, *class;
int error;
button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
@@ -353,7 +354,6 @@ static int acpi_button_add(struct acpi_device *device)
goto err_free_button;
}
- hid = acpi_device_hid(device);
name = acpi_device_name(device);
class = acpi_device_class(device);
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 3fe29e992be8..81514a4918cc 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -725,6 +725,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data)
complete_dock(ds);
dock_event(ds, event, DOCK_EVENT);
dock_lock(ds, 1);
+ acpi_update_gpes();
break;
}
if (dock_present(ds) || dock_in_progress(ds))
@@ -929,7 +930,7 @@ static struct attribute_group dock_attribute_group = {
* allocated and initialize a new dock station device. Find all devices
* that are on the dock station, and register for dock event notifications.
*/
-static int dock_add(acpi_handle handle)
+static int __init dock_add(acpi_handle handle)
{
int ret, id;
struct dock_station ds, *dock_station;
@@ -1023,7 +1024,7 @@ static int dock_remove(struct dock_station *ds)
*
* This is called by acpi_walk_namespace to look for dock stations.
*/
-static acpi_status
+static __init acpi_status
find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
{
if (is_dock(handle))
@@ -1032,7 +1033,7 @@ find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
-static acpi_status
+static __init acpi_status
find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
{
/* If bay is a dock, it's already handled */
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index f31291ba94d0..372ff80b7b0c 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -83,6 +83,11 @@ enum {
EC_FLAGS_BLOCKED, /* Transactions are blocked */
};
+/* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */
+static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY;
+module_param(ec_delay, uint, 0644);
+MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes");
+
/* If we find an EC via the ECDT, we need to keep a ptr to its context */
/* External interfaces use first EC only, so remember */
typedef int (*acpi_ec_query_func) (void *data);
@@ -210,7 +215,7 @@ static int ec_poll(struct acpi_ec *ec)
int repeat = 2; /* number of command restarts */
while (repeat--) {
unsigned long delay = jiffies +
- msecs_to_jiffies(ACPI_EC_DELAY);
+ msecs_to_jiffies(ec_delay);
do {
/* don't sleep with disabled interrupts */
if (EC_FLAGS_MSI || irqs_disabled()) {
@@ -265,7 +270,7 @@ static int ec_check_ibf0(struct acpi_ec *ec)
static int ec_wait_ibf0(struct acpi_ec *ec)
{
- unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
+ unsigned long delay = jiffies + msecs_to_jiffies(ec_delay);
/* interrupt wait manually if GPE mode is not active */
while (time_before(jiffies, delay))
if (wait_event_timeout(ec->wait, ec_check_ibf0(ec),
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index d94d2953c974..60049080c869 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -27,8 +27,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <linux/thermal.h>
#include <acpi/acpi_bus.h>
@@ -119,122 +117,6 @@ static struct thermal_cooling_device_ops fan_cooling_ops = {
};
/* --------------------------------------------------------------------------
- FS Interface (/proc)
- -------------------------------------------------------------------------- */
-#ifdef CONFIG_ACPI_PROCFS
-
-static struct proc_dir_entry *acpi_fan_dir;
-
-static int acpi_fan_read_state(struct seq_file *seq, void *offset)
-{
- struct acpi_device *device = seq->private;
- int state = 0;
-
-
- if (device) {
- if (acpi_bus_get_power(device->handle, &state))
- seq_printf(seq, "status: ERROR\n");
- else
- seq_printf(seq, "status: %s\n",
- !state ? "on" : "off");
- }
- return 0;
-}
-
-static int acpi_fan_state_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_fan_read_state, PDE(inode)->data);
-}
-
-static ssize_t
-acpi_fan_write_state(struct file *file, const char __user * buffer,
- size_t count, loff_t * ppos)
-{
- int result = 0;
- struct seq_file *m = file->private_data;
- struct acpi_device *device = m->private;
- char state_string[3] = { '\0' };
-
- if (count > sizeof(state_string) - 1)
- return -EINVAL;
-
- if (copy_from_user(state_string, buffer, count))
- return -EFAULT;
-
- state_string[count] = '\0';
- if ((state_string[0] < '0') || (state_string[0] > '3'))
- return -EINVAL;
- if (state_string[1] == '\n')
- state_string[1] = '\0';
- if (state_string[1] != '\0')
- return -EINVAL;
-
- result = acpi_bus_set_power(device->handle,
- simple_strtoul(state_string, NULL, 0));
- if (result)
- return result;
-
- return count;
-}
-
-static const struct file_operations acpi_fan_state_ops = {
- .open = acpi_fan_state_open_fs,
- .read = seq_read,
- .write = acpi_fan_write_state,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static int acpi_fan_add_fs(struct acpi_device *device)
-{
- struct proc_dir_entry *entry = NULL;
-
-
- if (!device)
- return -EINVAL;
-
- if (!acpi_device_dir(device)) {
- acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
- acpi_fan_dir);
- if (!acpi_device_dir(device))
- return -ENODEV;
- }
-
- /* 'status' [R/W] */
- entry = proc_create_data(ACPI_FAN_FILE_STATE,
- S_IFREG | S_IRUGO | S_IWUSR,
- acpi_device_dir(device),
- &acpi_fan_state_ops,
- device);
- if (!entry)
- return -ENODEV;
- return 0;
-}
-
-static int acpi_fan_remove_fs(struct acpi_device *device)
-{
-
- if (acpi_device_dir(device)) {
- remove_proc_entry(ACPI_FAN_FILE_STATE, acpi_device_dir(device));
- remove_proc_entry(acpi_device_bid(device), acpi_fan_dir);
- acpi_device_dir(device) = NULL;
- }
-
- return 0;
-}
-#else
-static int acpi_fan_add_fs(struct acpi_device *device)
-{
- return 0;
-}
-
-static int acpi_fan_remove_fs(struct acpi_device *device)
-{
- return 0;
-}
-#endif
-/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
@@ -284,10 +166,6 @@ static int acpi_fan_add(struct acpi_device *device)
dev_err(&device->dev, "Failed to create sysfs link "
"'device'\n");
- result = acpi_fan_add_fs(device);
- if (result)
- goto end;
-
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
acpi_device_name(device), acpi_device_bid(device),
!device->power.state ? "on" : "off");
@@ -303,7 +181,6 @@ static int acpi_fan_remove(struct acpi_device *device, int type)
if (!device || !cdev)
return -EINVAL;
- acpi_fan_remove_fs(device);
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
sysfs_remove_link(&cdev->device.kobj, "device");
thermal_cooling_device_unregister(cdev);
@@ -347,19 +224,9 @@ static int __init acpi_fan_init(void)
{
int result = 0;
-#ifdef CONFIG_ACPI_PROCFS
- acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
- if (!acpi_fan_dir)
- return -ENODEV;
-#endif
-
result = acpi_bus_register_driver(&acpi_fan_driver);
- if (result < 0) {
-#ifdef CONFIG_ACPI_PROCFS
- remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
-#endif
+ if (result < 0)
return -ENODEV;
- }
return 0;
}
@@ -369,10 +236,6 @@ static void __exit acpi_fan_exit(void)
acpi_bus_unregister_driver(&acpi_fan_driver);
-#ifdef CONFIG_ACPI_PROCFS
- remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
-#endif
-
return;
}
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 65b25a303b86..966feddf6b1b 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -95,8 +95,25 @@ struct acpi_res_list {
static LIST_HEAD(resource_list_head);
static DEFINE_SPINLOCK(acpi_res_lock);
+/*
+ * This list of permanent mappings is for memory that may be accessed from
+ * interrupt context, where we can't do the ioremap().
+ */
+struct acpi_ioremap {
+ struct list_head list;
+ void __iomem *virt;
+ acpi_physical_address phys;
+ acpi_size size;
+ struct kref ref;
+};
+
+static LIST_HEAD(acpi_ioremaps);
+static DEFINE_SPINLOCK(acpi_ioremap_lock);
+
#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */
-static char osi_additional_string[OSI_STRING_LENGTH_MAX];
+static char osi_setup_string[OSI_STRING_LENGTH_MAX];
+
+static void __init acpi_osi_setup_late(void);
/*
* The story of _OSI(Linux)
@@ -138,6 +155,20 @@ static struct osi_linux {
unsigned int known:1;
} osi_linux = { 0, 0, 0, 0};
+static u32 acpi_osi_handler(acpi_string interface, u32 supported)
+{
+ if (!strcmp("Linux", interface)) {
+
+ printk(KERN_NOTICE FW_BUG PREFIX
+ "BIOS _OSI(Linux) query %s%s\n",
+ osi_linux.enable ? "honored" : "ignored",
+ osi_linux.cmdline ? " via cmdline" :
+ osi_linux.dmi ? " via DMI" : "");
+ }
+
+ return supported;
+}
+
static void __init acpi_request_region (struct acpi_generic_address *addr,
unsigned int length, char *desc)
{
@@ -185,36 +216,6 @@ static int __init acpi_reserve_resources(void)
}
device_initcall(acpi_reserve_resources);
-acpi_status __init acpi_os_initialize(void)
-{
- return AE_OK;
-}
-
-acpi_status acpi_os_initialize1(void)
-{
- kacpid_wq = create_workqueue("kacpid");
- kacpi_notify_wq = create_workqueue("kacpi_notify");
- kacpi_hotplug_wq = create_workqueue("kacpi_hotplug");
- BUG_ON(!kacpid_wq);
- BUG_ON(!kacpi_notify_wq);
- BUG_ON(!kacpi_hotplug_wq);
- return AE_OK;
-}
-
-acpi_status acpi_os_terminate(void)
-{
- if (acpi_irq_handler) {
- acpi_os_remove_interrupt_handler(acpi_irq_irq,
- acpi_irq_handler);
- }
-
- destroy_workqueue(kacpid_wq);
- destroy_workqueue(kacpi_notify_wq);
- destroy_workqueue(kacpi_hotplug_wq);
-
- return AE_OK;
-}
-
void acpi_os_printf(const char *fmt, ...)
{
va_list args;
@@ -260,29 +261,135 @@ acpi_physical_address __init acpi_os_get_root_pointer(void)
}
}
+/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */
+static struct acpi_ioremap *
+acpi_map_lookup(acpi_physical_address phys, acpi_size size)
+{
+ struct acpi_ioremap *map;
+
+ list_for_each_entry_rcu(map, &acpi_ioremaps, list)
+ if (map->phys <= phys &&
+ phys + size <= map->phys + map->size)
+ return map;
+
+ return NULL;
+}
+
+/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */
+static void __iomem *
+acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size)
+{
+ struct acpi_ioremap *map;
+
+ map = acpi_map_lookup(phys, size);
+ if (map)
+ return map->virt + (phys - map->phys);
+
+ return NULL;
+}
+
+/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */
+static struct acpi_ioremap *
+acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
+{
+ struct acpi_ioremap *map;
+
+ list_for_each_entry_rcu(map, &acpi_ioremaps, list)
+ if (map->virt <= virt &&
+ virt + size <= map->virt + map->size)
+ return map;
+
+ return NULL;
+}
+
void __iomem *__init_refok
acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
{
+ struct acpi_ioremap *map, *tmp_map;
+ unsigned long flags, pg_sz;
+ void __iomem *virt;
+ phys_addr_t pg_off;
+
if (phys > ULONG_MAX) {
printk(KERN_ERR PREFIX "Cannot map memory that high\n");
return NULL;
}
- if (acpi_gbl_permanent_mmap)
- /*
- * ioremap checks to ensure this is in reserved space
- */
- return ioremap((unsigned long)phys, size);
- else
+
+ if (!acpi_gbl_permanent_mmap)
return __acpi_map_table((unsigned long)phys, size);
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map)
+ return NULL;
+
+ pg_off = round_down(phys, PAGE_SIZE);
+ pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off;
+ virt = ioremap(pg_off, pg_sz);
+ if (!virt) {
+ kfree(map);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&map->list);
+ map->virt = virt;
+ map->phys = pg_off;
+ map->size = pg_sz;
+ kref_init(&map->ref);
+
+ spin_lock_irqsave(&acpi_ioremap_lock, flags);
+ /* Check if page has already been mapped. */
+ tmp_map = acpi_map_lookup(phys, size);
+ if (tmp_map) {
+ kref_get(&tmp_map->ref);
+ spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
+ iounmap(map->virt);
+ kfree(map);
+ return tmp_map->virt + (phys - tmp_map->phys);
+ }
+ list_add_tail_rcu(&map->list, &acpi_ioremaps);
+ spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
+
+ return map->virt + (phys - map->phys);
}
EXPORT_SYMBOL_GPL(acpi_os_map_memory);
+static void acpi_kref_del_iomap(struct kref *ref)
+{
+ struct acpi_ioremap *map;
+
+ map = container_of(ref, struct acpi_ioremap, ref);
+ list_del_rcu(&map->list);
+}
+
void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
{
- if (acpi_gbl_permanent_mmap)
- iounmap(virt);
- else
+ struct acpi_ioremap *map;
+ unsigned long flags;
+ int del;
+
+ if (!acpi_gbl_permanent_mmap) {
__acpi_unmap_table(virt, size);
+ return;
+ }
+
+ spin_lock_irqsave(&acpi_ioremap_lock, flags);
+ map = acpi_map_lookup_virt(virt, size);
+ if (!map) {
+ spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
+ printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt);
+ dump_stack();
+ return;
+ }
+
+ del = kref_put(&map->ref, acpi_kref_del_iomap);
+ spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
+
+ if (!del)
+ return;
+
+ synchronize_rcu();
+ iounmap(map->virt);
+ kfree(map);
}
EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
@@ -292,6 +399,44 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
__acpi_unmap_table(virt, size);
}
+int acpi_os_map_generic_address(struct acpi_generic_address *addr)
+{
+ void __iomem *virt;
+
+ if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ return 0;
+
+ if (!addr->address || !addr->bit_width)
+ return -EINVAL;
+
+ virt = acpi_os_map_memory(addr->address, addr->bit_width / 8);
+ if (!virt)
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_os_map_generic_address);
+
+void acpi_os_unmap_generic_address(struct acpi_generic_address *addr)
+{
+ void __iomem *virt;
+ unsigned long flags;
+ acpi_size size = addr->bit_width / 8;
+
+ if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ return;
+
+ if (!addr->address || !addr->bit_width)
+ return;
+
+ spin_lock_irqsave(&acpi_ioremap_lock, flags);
+ virt = acpi_map_vaddr_lookup(addr->address, size);
+ spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
+
+ acpi_os_unmap_memory(virt, size);
+}
+EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address);
+
#ifdef ACPI_FUTURE_USAGE
acpi_status
acpi_os_get_physical_address(void *virt, acpi_physical_address * phys)
@@ -495,8 +640,15 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width)
{
u32 dummy;
void __iomem *virt_addr;
-
- virt_addr = ioremap(phys_addr, width);
+ int size = width / 8, unmap = 0;
+
+ rcu_read_lock();
+ virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
+ rcu_read_unlock();
+ if (!virt_addr) {
+ virt_addr = ioremap(phys_addr, size);
+ unmap = 1;
+ }
if (!value)
value = &dummy;
@@ -514,7 +666,8 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width)
BUG();
}
- iounmap(virt_addr);
+ if (unmap)
+ iounmap(virt_addr);
return AE_OK;
}
@@ -523,8 +676,15 @@ acpi_status
acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width)
{
void __iomem *virt_addr;
-
- virt_addr = ioremap(phys_addr, width);
+ int size = width / 8, unmap = 0;
+
+ rcu_read_lock();
+ virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
+ rcu_read_unlock();
+ if (!virt_addr) {
+ virt_addr = ioremap(phys_addr, size);
+ unmap = 1;
+ }
switch (width) {
case 8:
@@ -540,16 +700,18 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width)
BUG();
}
- iounmap(virt_addr);
+ if (unmap)
+ iounmap(virt_addr);
return AE_OK;
}
acpi_status
acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
- u32 *value, u32 width)
+ u64 *value, u32 width)
{
int result, size;
+ u32 value32;
if (!value)
return AE_BAD_PARAMETER;
@@ -570,7 +732,8 @@ acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
result = raw_pci_read(pci_id->segment, pci_id->bus,
PCI_DEVFN(pci_id->device, pci_id->function),
- reg, size, value);
+ reg, size, &value32);
+ *value = value32;
return (result ? AE_ERROR : AE_OK);
}
@@ -602,74 +765,6 @@ acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg,
return (result ? AE_ERROR : AE_OK);
}
-/* TODO: Change code to take advantage of driver model more */
-static void acpi_os_derive_pci_id_2(acpi_handle rhandle, /* upper bound */
- acpi_handle chandle, /* current node */
- struct acpi_pci_id **id,
- int *is_bridge, u8 * bus_number)
-{
- acpi_handle handle;
- struct acpi_pci_id *pci_id = *id;
- acpi_status status;
- unsigned long long temp;
- acpi_object_type type;
-
- acpi_get_parent(chandle, &handle);
- if (handle != rhandle) {
- acpi_os_derive_pci_id_2(rhandle, handle, &pci_id, is_bridge,
- bus_number);
-
- status = acpi_get_type(handle, &type);
- if ((ACPI_FAILURE(status)) || (type != ACPI_TYPE_DEVICE))
- return;
-
- status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL,
- &temp);
- if (ACPI_SUCCESS(status)) {
- u32 val;
- pci_id->device = ACPI_HIWORD(ACPI_LODWORD(temp));
- pci_id->function = ACPI_LOWORD(ACPI_LODWORD(temp));
-
- if (*is_bridge)
- pci_id->bus = *bus_number;
-
- /* any nicer way to get bus number of bridge ? */
- status =
- acpi_os_read_pci_configuration(pci_id, 0x0e, &val,
- 8);
- if (ACPI_SUCCESS(status)
- && ((val & 0x7f) == 1 || (val & 0x7f) == 2)) {
- status =
- acpi_os_read_pci_configuration(pci_id, 0x18,
- &val, 8);
- if (!ACPI_SUCCESS(status)) {
- /* Certainly broken... FIX ME */
- return;
- }
- *is_bridge = 1;
- pci_id->bus = val;
- status =
- acpi_os_read_pci_configuration(pci_id, 0x19,
- &val, 8);
- if (ACPI_SUCCESS(status)) {
- *bus_number = val;
- }
- } else
- *is_bridge = 0;
- }
- }
-}
-
-void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */
- acpi_handle chandle, /* current node */
- struct acpi_pci_id **id)
-{
- int is_bridge = 1;
- u8 bus_number = (*id)->bus;
-
- acpi_os_derive_pci_id_2(rhandle, chandle, id, &is_bridge, &bus_number);
-}
-
static void acpi_os_execute_deferred(struct work_struct *work)
{
struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
@@ -780,16 +875,6 @@ void acpi_os_wait_events_complete(void *context)
EXPORT_SYMBOL(acpi_os_wait_events_complete);
/*
- * Allocate the memory for a spinlock and initialize it.
- */
-acpi_status acpi_os_create_lock(acpi_spinlock * handle)
-{
- spin_lock_init(*handle);
-
- return AE_OK;
-}
-
-/*
* Deallocate the memory for a spinlock.
*/
void acpi_os_delete_lock(acpi_spinlock handle)
@@ -977,6 +1062,12 @@ static void __init set_osi_linux(unsigned int enable)
printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n",
enable ? "Add": "Delet");
}
+
+ if (osi_linux.enable)
+ acpi_osi_setup("Linux");
+ else
+ acpi_osi_setup("!Linux");
+
return;
}
@@ -1011,21 +1102,33 @@ void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d)
* string starting with '!' disables that string
* otherwise string is added to list, augmenting built-in strings
*/
-int __init acpi_osi_setup(char *str)
+static void __init acpi_osi_setup_late(void)
{
- if (str == NULL || *str == '\0') {
- printk(KERN_INFO PREFIX "_OSI method disabled\n");
- acpi_gbl_create_osi_method = FALSE;
- } else if (!strcmp("!Linux", str)) {
+ char *str = osi_setup_string;
+
+ if (*str == '\0')
+ return;
+
+ if (!strcmp("!Linux", str)) {
acpi_cmdline_osi_linux(0); /* !enable */
} else if (*str == '!') {
- if (acpi_osi_invalidate(++str) == AE_OK)
+ if (acpi_remove_interface(++str) == AE_OK)
printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str);
} else if (!strcmp("Linux", str)) {
acpi_cmdline_osi_linux(1); /* enable */
- } else if (*osi_additional_string == '\0') {
- strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX);
- printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str);
+ } else {
+ if (acpi_install_interface(str) == AE_OK)
+ printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str);
+ }
+}
+
+int __init acpi_osi_setup(char *str)
+{
+ if (str == NULL || *str == '\0') {
+ printk(KERN_INFO PREFIX "_OSI method disabled\n");
+ acpi_gbl_create_osi_method = FALSE;
+ } else {
+ strncpy(osi_setup_string, str, OSI_STRING_LENGTH_MAX);
}
return 1;
@@ -1152,21 +1255,6 @@ int acpi_check_region(resource_size_t start, resource_size_t n,
}
EXPORT_SYMBOL(acpi_check_region);
-int acpi_check_mem_region(resource_size_t start, resource_size_t n,
- const char *name)
-{
- struct resource res = {
- .start = start,
- .end = start + n - 1,
- .name = name,
- .flags = IORESOURCE_MEM,
- };
-
- return acpi_check_resource_conflict(&res);
-
-}
-EXPORT_SYMBOL(acpi_check_mem_region);
-
/*
* Let drivers know whether the resource checks are effective
*/
@@ -1282,38 +1370,6 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object)
return (AE_OK);
}
-/******************************************************************************
- *
- * FUNCTION: acpi_os_validate_interface
- *
- * PARAMETERS: interface - Requested interface to be validated
- *
- * RETURN: AE_OK if interface is supported, AE_SUPPORT otherwise
- *
- * DESCRIPTION: Match an interface string to the interfaces supported by the
- * host. Strings originate from an AML call to the _OSI method.
- *
- *****************************************************************************/
-
-acpi_status
-acpi_os_validate_interface (char *interface)
-{
- if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX))
- return AE_OK;
- if (!strcmp("Linux", interface)) {
-
- printk(KERN_NOTICE PREFIX
- "BIOS _OSI(Linux) query %s%s\n",
- osi_linux.enable ? "honored" : "ignored",
- osi_linux.cmdline ? " via cmdline" :
- osi_linux.dmi ? " via DMI" : "");
-
- if (osi_linux.enable)
- return AE_OK;
- }
- return AE_SUPPORT;
-}
-
static inline int acpi_res_list_add(struct acpi_res_list *res)
{
struct acpi_res_list *res_list_elem;
@@ -1462,5 +1518,46 @@ acpi_os_validate_address (
}
return AE_OK;
}
-
#endif
+
+acpi_status __init acpi_os_initialize(void)
+{
+ acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1a_event_block);
+ acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
+ acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block);
+ acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block);
+
+ return AE_OK;
+}
+
+acpi_status acpi_os_initialize1(void)
+{
+ kacpid_wq = create_workqueue("kacpid");
+ kacpi_notify_wq = create_workqueue("kacpi_notify");
+ kacpi_hotplug_wq = create_workqueue("kacpi_hotplug");
+ BUG_ON(!kacpid_wq);
+ BUG_ON(!kacpi_notify_wq);
+ BUG_ON(!kacpi_hotplug_wq);
+ acpi_install_interface_handler(acpi_osi_handler);
+ acpi_osi_setup_late();
+ return AE_OK;
+}
+
+acpi_status acpi_os_terminate(void)
+{
+ if (acpi_irq_handler) {
+ acpi_os_remove_interrupt_handler(acpi_irq_irq,
+ acpi_irq_handler);
+ }
+
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe1_block);
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block);
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
+ acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block);
+
+ destroy_workqueue(kacpid_wq);
+ destroy_workqueue(kacpi_notify_wq);
+ destroy_workqueue(kacpi_hotplug_wq);
+
+ return AE_OK;
+}
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index e4804fb05e23..f907cfbfa13c 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -32,7 +32,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
#include <linux/pci.h>
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
index 8d47a5846aeb..9ff80a7e9f6a 100644
--- a/drivers/acpi/pci_link.c
+++ b/drivers/acpi/pci_link.c
@@ -34,7 +34,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
#include <linux/pci.h>
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 3ba8d1f44a73..96668ad09622 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -27,7 +27,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 844c155aeb0f..67dedeed144c 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -80,18 +80,13 @@ static struct acpi_driver acpi_power_driver = {
},
};
-struct acpi_power_reference {
- struct list_head node;
- struct acpi_device *device;
-};
-
struct acpi_power_resource {
struct acpi_device * device;
acpi_bus_id name;
u32 system_level;
u32 order;
+ unsigned int ref_count;
struct mutex resource_lock;
- struct list_head reference;
};
static struct list_head acpi_power_resource_list;
@@ -184,101 +179,89 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
return result;
}
-static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
+static int __acpi_power_on(struct acpi_power_resource *resource)
{
- int result = 0;
- int found = 0;
acpi_status status = AE_OK;
- struct acpi_power_resource *resource = NULL;
- struct list_head *node, *next;
- struct acpi_power_reference *ref;
+ status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ /* Update the power resource's _device_ power state */
+ resource->device->power.state = ACPI_STATE_D0;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
+ resource->name));
+
+ return 0;
+}
+
+static int acpi_power_on(acpi_handle handle)
+{
+ int result = 0;
+ struct acpi_power_resource *resource = NULL;
result = acpi_power_get_context(handle, &resource);
if (result)
return result;
mutex_lock(&resource->resource_lock);
- list_for_each_safe(node, next, &resource->reference) {
- ref = container_of(node, struct acpi_power_reference, node);
- if (dev->handle == ref->device->handle) {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
- dev->pnp.bus_id, resource->name));
- found = 1;
- break;
- }
- }
- if (!found) {
- ref = kmalloc(sizeof (struct acpi_power_reference),
- irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
- if (!ref) {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n"));
- mutex_unlock(&resource->resource_lock);
- return -ENOMEM;
- }
- list_add_tail(&ref->node, &resource->reference);
- ref->device = dev;
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
- dev->pnp.bus_id, resource->name));
+ if (resource->ref_count++) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Power resource [%s] already on",
+ resource->name));
+ } else {
+ result = __acpi_power_on(resource);
}
- mutex_unlock(&resource->resource_lock);
- status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
- /* Update the power resource's _device_ power state */
- resource->device->power.state = ACPI_STATE_D0;
+ mutex_unlock(&resource->resource_lock);
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
- resource->name));
return 0;
}
-static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
+static int acpi_power_off_device(acpi_handle handle)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_power_resource *resource = NULL;
- struct list_head *node, *next;
- struct acpi_power_reference *ref;
result = acpi_power_get_context(handle, &resource);
if (result)
return result;
mutex_lock(&resource->resource_lock);
- list_for_each_safe(node, next, &resource->reference) {
- ref = container_of(node, struct acpi_power_reference, node);
- if (dev->handle == ref->device->handle) {
- list_del(&ref->node);
- kfree(ref);
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n",
- dev->pnp.bus_id, resource->name));
- break;
- }
+
+ if (!resource->ref_count) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Power resource [%s] already off",
+ resource->name));
+ goto unlock;
}
- if (!list_empty(&resource->reference)) {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n",
- resource->name));
- mutex_unlock(&resource->resource_lock);
- return 0;
+ if (--resource->ref_count) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Power resource [%s] still in use\n",
+ resource->name));
+ goto unlock;
}
- mutex_unlock(&resource->resource_lock);
status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
- if (ACPI_FAILURE(status))
- return -ENODEV;
+ if (ACPI_FAILURE(status)) {
+ result = -ENODEV;
+ } else {
+ /* Update the power resource's _device_ power state */
+ resource->device->power.state = ACPI_STATE_D3;
- /* Update the power resource's _device_ power state */
- resource->device->power.state = ACPI_STATE_D3;
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Power resource [%s] turned off\n",
+ resource->name));
+ }
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
- resource->name));
+ unlock:
+ mutex_unlock(&resource->resource_lock);
- return 0;
+ return result;
}
/**
@@ -364,7 +347,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
/* Open power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) {
- int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
+ int ret = acpi_power_on(dev->wakeup.resources.handles[i]);
if (ret) {
printk(KERN_ERR PREFIX "Transition power state\n");
dev->wakeup.flags.valid = 0;
@@ -420,7 +403,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
/* Close power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) {
int ret = acpi_power_off_device(
- dev->wakeup.resources.handles[i], dev);
+ dev->wakeup.resources.handles[i]);
if (ret) {
printk(KERN_ERR PREFIX "Transition power state\n");
dev->wakeup.flags.valid = 0;
@@ -500,7 +483,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
* (e.g. so the device doesn't lose power while transitioning).
*/
for (i = 0; i < tl->count; i++) {
- result = acpi_power_on(tl->handles[i], device);
+ result = acpi_power_on(tl->handles[i]);
if (result)
goto end;
}
@@ -513,7 +496,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
* Then we dereference all power resources used in the current list.
*/
for (i = 0; i < cl->count; i++) {
- result = acpi_power_off_device(cl->handles[i], device);
+ result = acpi_power_off_device(cl->handles[i]);
if (result)
goto end;
}
@@ -551,7 +534,6 @@ static int acpi_power_add(struct acpi_device *device)
resource->device = device;
mutex_init(&resource->resource_lock);
- INIT_LIST_HEAD(&resource->reference);
strcpy(resource->name, device->pnp.bus_id);
strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
@@ -594,22 +576,14 @@ static int acpi_power_add(struct acpi_device *device)
static int acpi_power_remove(struct acpi_device *device, int type)
{
- struct acpi_power_resource *resource = NULL;
- struct list_head *node, *next;
+ struct acpi_power_resource *resource;
-
- if (!device || !acpi_driver_data(device))
+ if (!device)
return -EINVAL;
resource = acpi_driver_data(device);
-
- mutex_lock(&resource->resource_lock);
- list_for_each_safe(node, next, &resource->reference) {
- struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
- list_del(&ref->node);
- kfree(ref);
- }
- mutex_unlock(&resource->resource_lock);
+ if (!resource)
+ return -EINVAL;
kfree(resource);
@@ -619,29 +593,28 @@ static int acpi_power_remove(struct acpi_device *device, int type)
static int acpi_power_resume(struct acpi_device *device)
{
int result = 0, state;
- struct acpi_power_resource *resource = NULL;
- struct acpi_power_reference *ref;
+ struct acpi_power_resource *resource;
- if (!device || !acpi_driver_data(device))
+ if (!device)
return -EINVAL;
resource = acpi_driver_data(device);
+ if (!resource)
+ return -EINVAL;
+
+ mutex_lock(&resource->resource_lock);
result = acpi_power_get_state(device->handle, &state);
if (result)
- return result;
+ goto unlock;
- mutex_lock(&resource->resource_lock);
- if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
- !list_empty(&resource->reference)) {
- ref = container_of(resource->reference.next, struct acpi_power_reference, node);
- mutex_unlock(&resource->resource_lock);
- result = acpi_power_on(device->handle, ref->device);
- return result;
- }
+ if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count)
+ result = __acpi_power_on(resource);
+ unlock:
mutex_unlock(&resource->resource_lock);
- return 0;
+
+ return result;
}
int __init acpi_power_init(void)
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 347eb21b2353..85e48047d7b0 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -40,8 +40,10 @@
#include <linux/pm.h>
#include <linux/cpufreq.h>
#include <linux/cpu.h>
+#ifdef CONFIG_ACPI_PROCFS
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#endif
#include <linux/dmi.h>
#include <linux/moduleparam.h>
#include <linux/cpuidle.h>
@@ -244,6 +246,7 @@ static int acpi_processor_errata(struct acpi_processor *pr)
return result;
}
+#ifdef CONFIG_ACPI_PROCFS
static struct proc_dir_entry *acpi_processor_dir = NULL;
static int __cpuinit acpi_processor_add_fs(struct acpi_device *device)
@@ -280,7 +283,16 @@ static int acpi_processor_remove_fs(struct acpi_device *device)
return 0;
}
-
+#else
+static inline int acpi_processor_add_fs(struct acpi_device *device)
+{
+ return 0;
+}
+static inline int acpi_processor_remove_fs(struct acpi_device *device)
+{
+ return 0;
+}
+#endif
/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
@@ -842,9 +854,11 @@ static int __init acpi_processor_init(void)
memset(&errata, 0, sizeof(errata));
+#ifdef CONFIG_ACPI_PROCFS
acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir);
if (!acpi_processor_dir)
return -ENOMEM;
+#endif
if (!cpuidle_register_driver(&acpi_idle_driver)) {
printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n",
@@ -871,7 +885,9 @@ static int __init acpi_processor_init(void)
out_cpuidle:
cpuidle_unregister_driver(&acpi_idle_driver);
+#ifdef CONFIG_ACPI_PROCFS
remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+#endif
return result;
}
@@ -891,7 +907,9 @@ static void __exit acpi_processor_exit(void)
cpuidle_unregister_driver(&acpi_idle_driver);
+#ifdef CONFIG_ACPI_PROCFS
remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+#endif
return;
}
@@ -899,6 +917,4 @@ static void __exit acpi_processor_exit(void)
module_init(acpi_processor_init);
module_exit(acpi_processor_exit);
-EXPORT_SYMBOL(acpi_processor_set_thermal_limit);
-
MODULE_ALIAS("processor");
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index f4428e82b352..dcb38f8ddfda 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -64,7 +64,6 @@
#define ACPI_PROCESSOR_CLASS "processor"
#define _COMPONENT ACPI_PROCESSOR_COMPONENT
ACPI_MODULE_NAME("processor_idle");
-#define ACPI_PROCESSOR_FILE_POWER "power"
#define PM_TIMER_TICK_NS (1000000000ULL/PM_TIMER_FREQUENCY)
#define C2_OVERHEAD 1 /* 1us */
#define C3_OVERHEAD 1 /* 1us */
@@ -1013,7 +1012,6 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
state->exit_latency = cx->latency;
state->target_residency = cx->latency * latency_factor;
- state->power_usage = cx->power;
state->flags = 0;
switch (cx->type) {
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index 953b25fb9869..fde49b9b1d99 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -44,47 +44,6 @@
#define _COMPONENT ACPI_PROCESSOR_COMPONENT
ACPI_MODULE_NAME("processor_thermal");
-/* --------------------------------------------------------------------------
- Limit Interface
- -------------------------------------------------------------------------- */
-static int acpi_processor_apply_limit(struct acpi_processor *pr)
-{
- int result = 0;
- u16 px = 0;
- u16 tx = 0;
-
-
- if (!pr)
- return -EINVAL;
-
- if (!pr->flags.limit)
- return -ENODEV;
-
- if (pr->flags.throttling) {
- if (pr->limit.user.tx > tx)
- tx = pr->limit.user.tx;
- if (pr->limit.thermal.tx > tx)
- tx = pr->limit.thermal.tx;
-
- result = acpi_processor_set_throttling(pr, tx, false);
- if (result)
- goto end;
- }
-
- pr->limit.state.px = px;
- pr->limit.state.tx = tx;
-
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Processor [%d] limit set to (P%d:T%d)\n", pr->id,
- pr->limit.state.px, pr->limit.state.tx));
-
- end:
- if (result)
- printk(KERN_ERR PREFIX "Unable to set limit\n");
-
- return result;
-}
-
#ifdef CONFIG_CPU_FREQ
/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
@@ -107,36 +66,6 @@ static int cpu_has_cpufreq(unsigned int cpu)
return 1;
}
-static int acpi_thermal_cpufreq_increase(unsigned int cpu)
-{
- if (!cpu_has_cpufreq(cpu))
- return -ENODEV;
-
- if (per_cpu(cpufreq_thermal_reduction_pctg, cpu) <
- CPUFREQ_THERMAL_MAX_STEP) {
- per_cpu(cpufreq_thermal_reduction_pctg, cpu)++;
- cpufreq_update_policy(cpu);
- return 0;
- }
-
- return -ERANGE;
-}
-
-static int acpi_thermal_cpufreq_decrease(unsigned int cpu)
-{
- if (!cpu_has_cpufreq(cpu))
- return -ENODEV;
-
- if (per_cpu(cpufreq_thermal_reduction_pctg, cpu) >
- (CPUFREQ_THERMAL_MIN_STEP + 1))
- per_cpu(cpufreq_thermal_reduction_pctg, cpu)--;
- else
- per_cpu(cpufreq_thermal_reduction_pctg, cpu) = 0;
- cpufreq_update_policy(cpu);
- /* We reached max freq again and can leave passive mode */
- return !per_cpu(cpufreq_thermal_reduction_pctg, cpu);
-}
-
static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
@@ -238,113 +167,6 @@ static int acpi_thermal_cpufreq_decrease(unsigned int cpu)
#endif
-int acpi_processor_set_thermal_limit(acpi_handle handle, int type)
-{
- int result = 0;
- struct acpi_processor *pr = NULL;
- struct acpi_device *device = NULL;
- int tx = 0, max_tx_px = 0;
-
-
- if ((type < ACPI_PROCESSOR_LIMIT_NONE)
- || (type > ACPI_PROCESSOR_LIMIT_DECREMENT))
- return -EINVAL;
-
- result = acpi_bus_get_device(handle, &device);
- if (result)
- return result;
-
- pr = acpi_driver_data(device);
- if (!pr)
- return -ENODEV;
-
- /* Thermal limits are always relative to the current Px/Tx state. */
- if (pr->flags.throttling)
- pr->limit.thermal.tx = pr->throttling.state;
-
- /*
- * Our default policy is to only use throttling at the lowest
- * performance state.
- */
-
- tx = pr->limit.thermal.tx;
-
- switch (type) {
-
- case ACPI_PROCESSOR_LIMIT_NONE:
- do {
- result = acpi_thermal_cpufreq_decrease(pr->id);
- } while (!result);
- tx = 0;
- break;
-
- case ACPI_PROCESSOR_LIMIT_INCREMENT:
- /* if going up: P-states first, T-states later */
-
- result = acpi_thermal_cpufreq_increase(pr->id);
- if (!result)
- goto end;
- else if (result == -ERANGE)
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "At maximum performance state\n"));
-
- if (pr->flags.throttling) {
- if (tx == (pr->throttling.state_count - 1))
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "At maximum throttling state\n"));
- else
- tx++;
- }
- break;
-
- case ACPI_PROCESSOR_LIMIT_DECREMENT:
- /* if going down: T-states first, P-states later */
-
- if (pr->flags.throttling) {
- if (tx == 0) {
- max_tx_px = 1;
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "At minimum throttling state\n"));
- } else {
- tx--;
- goto end;
- }
- }
-
- result = acpi_thermal_cpufreq_decrease(pr->id);
- if (result) {
- /*
- * We only could get -ERANGE, 1 or 0.
- * In the first two cases we reached max freq again.
- */
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "At minimum performance state\n"));
- max_tx_px = 1;
- } else
- max_tx_px = 0;
-
- break;
- }
-
- end:
- if (pr->flags.throttling) {
- pr->limit.thermal.px = 0;
- pr->limit.thermal.tx = tx;
-
- result = acpi_processor_apply_limit(pr);
- if (result)
- printk(KERN_ERR PREFIX "Unable to set thermal limit\n");
-
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Thermal limit now (P%d:T%d)\n",
- pr->limit.thermal.px, pr->limit.thermal.tx));
- } else
- result = 0;
- if (max_tx_px)
- return 1;
- else
- return result;
-}
-
int acpi_processor_get_limit_info(struct acpi_processor *pr)
{
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index 730863855ed5..ff3632717c51 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -32,8 +32,10 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/cpufreq.h>
+#ifdef CONFIG_ACPI_PROCFS
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#endif
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -1214,6 +1216,7 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr)
return result;
}
+#ifdef CONFIG_ACPI_PROCFS
/* proc interface */
static int acpi_processor_throttling_seq_show(struct seq_file *seq,
void *offset)
@@ -1322,3 +1325,4 @@ const struct file_operations acpi_processor_throttling_fops = {
.llseek = seq_lseek,
.release = single_release,
};
+#endif
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 4ff76e8174eb..e5dbedb16bbf 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -40,10 +40,7 @@
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
-
-#ifdef CONFIG_ACPI_SYSFS_POWER
#include <linux/power_supply.h>
-#endif
#include "sbshc.h"
@@ -85,9 +82,7 @@ static const struct acpi_device_id sbs_device_ids[] = {
MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
struct acpi_battery {
-#ifdef CONFIG_ACPI_SYSFS_POWER
struct power_supply bat;
-#endif
struct acpi_sbs *sbs;
#ifdef CONFIG_ACPI_PROCFS_POWER
struct proc_dir_entry *proc_entry;
@@ -120,9 +115,7 @@ struct acpi_battery {
#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat);
struct acpi_sbs {
-#ifdef CONFIG_ACPI_SYSFS_POWER
struct power_supply charger;
-#endif
struct acpi_device *device;
struct acpi_smb_hc *hc;
struct mutex lock;
@@ -166,7 +159,6 @@ static inline int acpi_battery_scale(struct acpi_battery *battery)
acpi_battery_ipscale(battery);
}
-#ifdef CONFIG_ACPI_SYSFS_POWER
static int sbs_get_ac_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -313,7 +305,6 @@ static enum power_supply_property sbs_energy_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
-#endif
/* --------------------------------------------------------------------------
Smart Battery System Management
@@ -449,7 +440,6 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs)
return result;
}
-#ifdef CONFIG_ACPI_SYSFS_POWER
static ssize_t acpi_battery_alarm_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -479,7 +469,6 @@ static struct device_attribute alarm_attr = {
.show = acpi_battery_alarm_show,
.store = acpi_battery_alarm_store,
};
-#endif
/* --------------------------------------------------------------------------
FS Interface (/proc/acpi)
@@ -798,7 +787,6 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
&acpi_battery_state_fops, &acpi_battery_alarm_fops,
battery);
#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
battery->bat.name = battery->name;
battery->bat.type = POWER_SUPPLY_TYPE_BATTERY;
if (!acpi_battery_mode(battery)) {
@@ -819,7 +807,6 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
goto end;
battery->have_sysfs_alarm = 1;
end:
-#endif
printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
battery->name, battery->present ? "present" : "absent");
@@ -828,17 +815,13 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
static void acpi_battery_remove(struct acpi_sbs *sbs, int id)
{
-#if defined(CONFIG_ACPI_SYSFS_POWER) || defined(CONFIG_ACPI_PROCFS_POWER)
struct acpi_battery *battery = &sbs->battery[id];
-#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
if (battery->bat.dev) {
if (battery->have_sysfs_alarm)
device_remove_file(battery->bat.dev, &alarm_attr);
power_supply_unregister(&battery->bat);
}
-#endif
#ifdef CONFIG_ACPI_PROCFS_POWER
if (battery->proc_entry)
acpi_sbs_remove_fs(&battery->proc_entry, acpi_battery_dir);
@@ -859,14 +842,12 @@ static int acpi_charger_add(struct acpi_sbs *sbs)
if (result)
goto end;
#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
sbs->charger.name = "sbs-charger";
sbs->charger.type = POWER_SUPPLY_TYPE_MAINS;
sbs->charger.properties = sbs_ac_props;
sbs->charger.num_properties = ARRAY_SIZE(sbs_ac_props);
sbs->charger.get_property = sbs_get_ac_property;
power_supply_register(&sbs->device->dev, &sbs->charger);
-#endif
printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line");
@@ -876,10 +857,8 @@ static int acpi_charger_add(struct acpi_sbs *sbs)
static void acpi_charger_remove(struct acpi_sbs *sbs)
{
-#ifdef CONFIG_ACPI_SYSFS_POWER
if (sbs->charger.dev)
power_supply_unregister(&sbs->charger);
-#endif
#ifdef CONFIG_ACPI_PROCFS_POWER
if (sbs->charger_entry)
acpi_sbs_remove_fs(&sbs->charger_entry, acpi_ac_dir);
@@ -900,9 +879,7 @@ static void acpi_sbs_callback(void *context)
ACPI_SBS_NOTIFY_STATUS,
sbs->charger_present);
#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE);
-#endif
}
if (sbs->manager_present) {
for (id = 0; id < MAX_SBS_BAT; ++id) {
@@ -919,9 +896,7 @@ static void acpi_sbs_callback(void *context)
ACPI_SBS_NOTIFY_STATUS,
bat->present);
#endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
kobject_uevent(&bat->bat.dev->kobj, KOBJ_CHANGE);
-#endif
}
}
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index b23825ecfa37..2b6c21d86b98 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -26,6 +26,8 @@ extern struct acpi_device *acpi_root;
#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent)
+static const char *dummy_hid = "device";
+
static LIST_HEAD(acpi_device_list);
static LIST_HEAD(acpi_bus_id_list);
DEFINE_MUTEX(acpi_device_lock);
@@ -49,6 +51,9 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
int count;
struct acpi_hardware_id *id;
+ if (list_empty(&acpi_dev->pnp.ids))
+ return 0;
+
len = snprintf(modalias, size, "acpi:");
size -= len;
@@ -202,13 +207,15 @@ static int acpi_device_setup_files(struct acpi_device *dev)
goto end;
}
- result = device_create_file(&dev->dev, &dev_attr_hid);
- if (result)
- goto end;
+ if (!list_empty(&dev->pnp.ids)) {
+ result = device_create_file(&dev->dev, &dev_attr_hid);
+ if (result)
+ goto end;
- result = device_create_file(&dev->dev, &dev_attr_modalias);
- if (result)
- goto end;
+ result = device_create_file(&dev->dev, &dev_attr_modalias);
+ if (result)
+ goto end;
+ }
/*
* If device has _EJ0, 'eject' file is created that is used to trigger
@@ -316,6 +323,9 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
struct acpi_device *acpi_dev = to_acpi_device(dev);
int len;
+ if (list_empty(&acpi_dev->pnp.ids))
+ return 0;
+
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
@@ -1010,10 +1020,13 @@ static int acpi_dock_match(struct acpi_device *device)
return acpi_get_handle(device->handle, "_DCK", &tmp);
}
-char *acpi_device_hid(struct acpi_device *device)
+const char *acpi_device_hid(struct acpi_device *device)
{
struct acpi_hardware_id *hid;
+ if (list_empty(&device->pnp.ids))
+ return dummy_hid;
+
hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list);
return hid->id;
}
@@ -1142,16 +1155,6 @@ static void acpi_device_set_id(struct acpi_device *device)
acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF);
break;
}
-
- /*
- * We build acpi_devices for some objects that don't have _HID or _CID,
- * e.g., PCI bridges and slots. Drivers can't bind to these objects,
- * but we do use them indirectly by traversing the acpi_device tree.
- * This generic ID isn't useful for driver binding, but it provides
- * the useful property that "every acpi_device has an ID."
- */
- if (list_empty(&device->pnp.ids))
- acpi_add_id(device, "device");
}
static int acpi_device_set_context(struct acpi_device *device)
@@ -1431,6 +1434,7 @@ EXPORT_SYMBOL(acpi_bus_add);
int acpi_bus_start(struct acpi_device *device)
{
struct acpi_bus_ops ops;
+ int result;
if (!device)
return -EINVAL;
@@ -1438,7 +1442,11 @@ int acpi_bus_start(struct acpi_device *device)
memset(&ops, 0, sizeof(ops));
ops.acpi_op_start = 1;
- return acpi_bus_scan(device->handle, &ops, NULL);
+ result = acpi_bus_scan(device->handle, &ops, NULL);
+
+ acpi_update_gpes();
+
+ return result;
}
EXPORT_SYMBOL(acpi_bus_start);
@@ -1552,6 +1560,8 @@ int __init acpi_scan_init(void)
if (result)
acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
+ else
+ acpi_update_gpes();
return result;
}
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 4754ff6e70e6..721d93b3ceee 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -25,7 +25,9 @@
#include "internal.h"
#include "sleep.h"
-u8 sleep_states[ACPI_S_STATE_COUNT];
+static u8 sleep_states[ACPI_S_STATE_COUNT];
+
+static u32 acpi_target_sleep_state = ACPI_STATE_S0;
static void acpi_sleep_tts_switch(u32 acpi_state)
{
@@ -79,8 +81,6 @@ static int acpi_sleep_prepare(u32 acpi_state)
}
#ifdef CONFIG_ACPI_SLEEP
-static u32 acpi_target_sleep_state = ACPI_STATE_S0;
-
/*
* The ACPI specification wants us to save NVS memory regions during hibernation
* and to restore them during the subsequent resume. Windows does that also for
@@ -419,6 +419,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Everex StepNote Series"),
},
},
+ {
+ .callback = init_nvs_nosave,
+ .ident = "Sony Vaio VPCEB1Z1E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1Z1E"),
+ },
+ },
{},
};
#endif /* CONFIG_SUSPEND */
@@ -562,7 +570,7 @@ int acpi_suspend(u32 acpi_state)
return -EINVAL;
}
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_OPS
/**
* acpi_pm_device_sleep_state - return preferred power state of ACPI device
* in the system sleep state given by %acpi_target_sleep_state
@@ -624,7 +632,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
* can wake the system. _S0W may be valid, too.
*/
if (acpi_target_sleep_state == ACPI_STATE_S0 ||
- (device_may_wakeup(dev) && adev->wakeup.state.enabled &&
+ (device_may_wakeup(dev) &&
adev->wakeup.sleep_state <= acpi_target_sleep_state)) {
acpi_status status;
@@ -632,7 +640,9 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
status = acpi_evaluate_integer(handle, acpi_method, NULL,
&d_max);
if (ACPI_FAILURE(status)) {
- d_max = d_min;
+ if (acpi_target_sleep_state != ACPI_STATE_S0 ||
+ status != AE_NOT_FOUND)
+ d_max = d_min;
} else if (d_max < d_min) {
/* Warn the user of the broken DSDT */
printk(KERN_WARNING "ACPI: Wrong value from %s\n",
@@ -646,7 +656,9 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
*d_min_p = d_min;
return d_max;
}
+#endif /* CONFIG_PM_OPS */
+#ifdef CONFIG_PM_SLEEP
/**
* acpi_pm_device_sleep_wake - enable or disable the system wake-up
* capability of given device
@@ -677,7 +689,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
return error;
}
-#endif
+#endif /* CONFIG_PM_SLEEP */
static void acpi_power_off_prepare(void)
{
@@ -702,7 +714,7 @@ static void acpi_power_off(void)
* paths through the BIOS, so disable _GTS and _BFS by default,
* but do speak up and offer the option to enable them.
*/
-void __init acpi_gts_bfs_check(void)
+static void __init acpi_gts_bfs_check(void)
{
acpi_handle dummy;
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
index d8821805c3bc..74d59c8f4678 100644
--- a/drivers/acpi/sleep.h
+++ b/drivers/acpi/sleep.h
@@ -1,5 +1,4 @@
-extern u8 sleep_states[];
extern int acpi_suspend(u32 state);
extern void acpi_enable_wakeup_devices(u8 sleep_state);
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 2f8f17131d9f..5a27b0a31315 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -37,12 +37,6 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
-
-#ifdef CONFIG_ACPI_PROCFS
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#endif
-
#include <linux/jiffies.h>
#include <linux/kmod.h>
#include <linux/reboot.h>
@@ -195,61 +189,6 @@ struct acpi_thermal {
struct mutex lock;
};
-#ifdef CONFIG_ACPI_PROCFS
-static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file);
-static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file);
-static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file);
-static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file);
-static ssize_t acpi_thermal_write_cooling_mode(struct file *,
- const char __user *, size_t,
- loff_t *);
-static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file);
-static ssize_t acpi_thermal_write_polling(struct file *, const char __user *,
- size_t, loff_t *);
-
-static const struct file_operations acpi_thermal_state_fops = {
- .owner = THIS_MODULE,
- .open = acpi_thermal_state_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations acpi_thermal_temp_fops = {
- .owner = THIS_MODULE,
- .open = acpi_thermal_temp_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations acpi_thermal_trip_fops = {
- .owner = THIS_MODULE,
- .open = acpi_thermal_trip_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations acpi_thermal_cooling_fops = {
- .owner = THIS_MODULE,
- .open = acpi_thermal_cooling_open_fs,
- .read = seq_read,
- .write = acpi_thermal_write_cooling_mode,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations acpi_thermal_polling_fops = {
- .owner = THIS_MODULE,
- .open = acpi_thermal_polling_open_fs,
- .read = seq_read,
- .write = acpi_thermal_write_polling,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif /* CONFIG_ACPI_PROCFS*/
-
/* --------------------------------------------------------------------------
Thermal Zone Management
-------------------------------------------------------------------------- */
@@ -958,358 +897,6 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
/* --------------------------------------------------------------------------
- FS Interface (/proc)
- -------------------------------------------------------------------------- */
-#ifdef CONFIG_ACPI_PROCFS
-static struct proc_dir_entry *acpi_thermal_dir;
-
-static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_thermal *tz = seq->private;
-
-
- if (!tz)
- goto end;
-
- seq_puts(seq, "state: ");
-
- if (!tz->state.critical && !tz->state.hot && !tz->state.passive
- && !tz->state.active)
- seq_puts(seq, "ok\n");
- else {
- if (tz->state.critical)
- seq_puts(seq, "critical ");
- if (tz->state.hot)
- seq_puts(seq, "hot ");
- if (tz->state.passive)
- seq_puts(seq, "passive ");
- if (tz->state.active)
- seq_printf(seq, "active[%d]", tz->state.active_index);
- seq_puts(seq, "\n");
- }
-
- end:
- return 0;
-}
-
-static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data);
-}
-
-static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset)
-{
- int result = 0;
- struct acpi_thermal *tz = seq->private;
-
-
- if (!tz)
- goto end;
-
- result = acpi_thermal_get_temperature(tz);
- if (result)
- goto end;
-
- seq_printf(seq, "temperature: %ld C\n",
- KELVIN_TO_CELSIUS(tz->temperature));
-
- end:
- return 0;
-}
-
-static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data);
-}
-
-static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_thermal *tz = seq->private;
- struct acpi_device *device;
- acpi_status status;
-
- int i = 0;
- int j = 0;
-
-
- if (!tz)
- goto end;
-
- if (tz->trips.critical.flags.valid)
- seq_printf(seq, "critical (S5): %ld C%s",
- KELVIN_TO_CELSIUS(tz->trips.critical.temperature),
- nocrt ? " <disabled>\n" : "\n");
-
- if (tz->trips.hot.flags.valid)
- seq_printf(seq, "hot (S4): %ld C%s",
- KELVIN_TO_CELSIUS(tz->trips.hot.temperature),
- nocrt ? " <disabled>\n" : "\n");
-
- if (tz->trips.passive.flags.valid) {
- seq_printf(seq,
- "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=",
- KELVIN_TO_CELSIUS(tz->trips.passive.temperature),
- tz->trips.passive.tc1, tz->trips.passive.tc2,
- tz->trips.passive.tsp);
- for (j = 0; j < tz->trips.passive.devices.count; j++) {
- status = acpi_bus_get_device(tz->trips.passive.devices.
- handles[j], &device);
- seq_printf(seq, "%4.4s ", status ? "" :
- acpi_device_bid(device));
- }
- seq_puts(seq, "\n");
- } else {
- seq_printf(seq, "passive (forced):");
- if (tz->thermal_zone->forced_passive)
- seq_printf(seq, " %i C\n",
- tz->thermal_zone->forced_passive / 1000);
- else
- seq_printf(seq, "<not set>\n");
- }
-
- for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
- if (!(tz->trips.active[i].flags.valid))
- break;
- seq_printf(seq, "active[%d]: %ld C: devices=",
- i,
- KELVIN_TO_CELSIUS(tz->trips.active[i].temperature));
- for (j = 0; j < tz->trips.active[i].devices.count; j++){
- status = acpi_bus_get_device(tz->trips.active[i].
- devices.handles[j],
- &device);
- seq_printf(seq, "%4.4s ", status ? "" :
- acpi_device_bid(device));
- }
- seq_puts(seq, "\n");
- }
-
- end:
- return 0;
-}
-
-static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data);
-}
-
-static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_thermal *tz = seq->private;
-
-
- if (!tz)
- goto end;
-
- if (!tz->flags.cooling_mode)
- seq_puts(seq, "<setting not supported>\n");
- else
- seq_puts(seq, "0 - Active; 1 - Passive\n");
-
- end:
- return 0;
-}
-
-static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_thermal_cooling_seq_show,
- PDE(inode)->data);
-}
-
-static ssize_t
-acpi_thermal_write_cooling_mode(struct file *file,
- const char __user * buffer,
- size_t count, loff_t * ppos)
-{
- struct seq_file *m = file->private_data;
- struct acpi_thermal *tz = m->private;
- int result = 0;
- char mode_string[12] = { '\0' };
-
-
- if (!tz || (count > sizeof(mode_string) - 1))
- return -EINVAL;
-
- if (!tz->flags.cooling_mode)
- return -ENODEV;
-
- if (copy_from_user(mode_string, buffer, count))
- return -EFAULT;
-
- mode_string[count] = '\0';
-
- result = acpi_thermal_set_cooling_mode(tz,
- simple_strtoul(mode_string, NULL,
- 0));
- if (result)
- return result;
-
- acpi_thermal_check(tz);
-
- return count;
-}
-
-static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_thermal *tz = seq->private;
-
-
- if (!tz)
- goto end;
-
- if (!tz->thermal_zone->polling_delay) {
- seq_puts(seq, "<polling disabled>\n");
- goto end;
- }
-
- seq_printf(seq, "polling frequency: %d seconds\n",
- (tz->thermal_zone->polling_delay / 1000));
-
- end:
- return 0;
-}
-
-static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_thermal_polling_seq_show,
- PDE(inode)->data);
-}
-
-static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds)
-{
- if (!tz)
- return -EINVAL;
-
- /* Convert value to deci-seconds */
- tz->polling_frequency = seconds * 10;
-
- tz->thermal_zone->polling_delay = seconds * 1000;
-
- if (tz->tz_enabled)
- thermal_zone_device_update(tz->thermal_zone);
-
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Polling frequency set to %lu seconds\n",
- tz->polling_frequency/10));
-
- return 0;
-}
-
-static ssize_t
-acpi_thermal_write_polling(struct file *file,
- const char __user * buffer,
- size_t count, loff_t * ppos)
-{
- struct seq_file *m = file->private_data;
- struct acpi_thermal *tz = m->private;
- int result = 0;
- char polling_string[12] = { '\0' };
- int seconds = 0;
-
-
- if (!tz || (count > sizeof(polling_string) - 1))
- return -EINVAL;
-
- if (copy_from_user(polling_string, buffer, count))
- return -EFAULT;
-
- polling_string[count] = '\0';
-
- seconds = simple_strtoul(polling_string, NULL, 0);
-
- result = acpi_thermal_set_polling(tz, seconds);
- if (result)
- return result;
-
- acpi_thermal_check(tz);
-
- return count;
-}
-
-static int acpi_thermal_add_fs(struct acpi_device *device)
-{
- struct proc_dir_entry *entry = NULL;
-
-
- if (!acpi_device_dir(device)) {
- acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
- acpi_thermal_dir);
- if (!acpi_device_dir(device))
- return -ENODEV;
- }
-
- /* 'state' [R] */
- entry = proc_create_data(ACPI_THERMAL_FILE_STATE,
- S_IRUGO, acpi_device_dir(device),
- &acpi_thermal_state_fops,
- acpi_driver_data(device));
- if (!entry)
- return -ENODEV;
-
- /* 'temperature' [R] */
- entry = proc_create_data(ACPI_THERMAL_FILE_TEMPERATURE,
- S_IRUGO, acpi_device_dir(device),
- &acpi_thermal_temp_fops,
- acpi_driver_data(device));
- if (!entry)
- return -ENODEV;
-
- /* 'trip_points' [R] */
- entry = proc_create_data(ACPI_THERMAL_FILE_TRIP_POINTS,
- S_IRUGO,
- acpi_device_dir(device),
- &acpi_thermal_trip_fops,
- acpi_driver_data(device));
- if (!entry)
- return -ENODEV;
-
- /* 'cooling_mode' [R/W] */
- entry = proc_create_data(ACPI_THERMAL_FILE_COOLING_MODE,
- S_IFREG | S_IRUGO | S_IWUSR,
- acpi_device_dir(device),
- &acpi_thermal_cooling_fops,
- acpi_driver_data(device));
- if (!entry)
- return -ENODEV;
-
- /* 'polling_frequency' [R/W] */
- entry = proc_create_data(ACPI_THERMAL_FILE_POLLING_FREQ,
- S_IFREG | S_IRUGO | S_IWUSR,
- acpi_device_dir(device),
- &acpi_thermal_polling_fops,
- acpi_driver_data(device));
- if (!entry)
- return -ENODEV;
- return 0;
-}
-
-static int acpi_thermal_remove_fs(struct acpi_device *device)
-{
-
- if (acpi_device_dir(device)) {
- remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
- acpi_device_dir(device));
- remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
- acpi_device_dir(device));
- remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
- acpi_device_dir(device));
- remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
- acpi_device_dir(device));
- remove_proc_entry(ACPI_THERMAL_FILE_STATE,
- acpi_device_dir(device));
- remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir);
- acpi_device_dir(device) = NULL;
- }
-
- return 0;
-}
-#else
-static inline int acpi_thermal_add_fs(struct acpi_device *device) { return 0; }
-static inline int acpi_thermal_remove_fs(struct acpi_device *device)
-{
- return 0;
-}
-#endif /* CONFIG_ACPI_PROCFS */
-/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
@@ -1428,17 +1015,11 @@ static int acpi_thermal_add(struct acpi_device *device)
if (result)
goto free_memory;
- result = acpi_thermal_add_fs(device);
- if (result)
- goto unregister_thermal_zone;
-
printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
acpi_device_name(device), acpi_device_bid(device),
KELVIN_TO_CELSIUS(tz->temperature));
goto end;
-unregister_thermal_zone:
- thermal_zone_device_unregister(tz->thermal_zone);
free_memory:
kfree(tz);
end:
@@ -1454,7 +1035,6 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
tz = acpi_driver_data(device);
- acpi_thermal_remove_fs(device);
acpi_thermal_unregister_thermal_zone(tz);
mutex_destroy(&tz->lock);
kfree(tz);
@@ -1580,19 +1160,9 @@ static int __init acpi_thermal_init(void)
return -ENODEV;
}
-#ifdef CONFIG_ACPI_PROCFS
- acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir);
- if (!acpi_thermal_dir)
- return -ENODEV;
-#endif
-
result = acpi_bus_register_driver(&acpi_thermal_driver);
- if (result < 0) {
-#ifdef CONFIG_ACPI_PROCFS
- remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
-#endif
+ if (result < 0)
return -ENODEV;
- }
return 0;
}
@@ -1602,10 +1172,6 @@ static void __exit acpi_thermal_exit(void)
acpi_bus_unregister_driver(&acpi_thermal_driver);
-#ifdef CONFIG_ACPI_PROCFS
- remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
-#endif
-
return;
}
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 67dec0c675aa..5cd0228d2daa 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -30,8 +30,6 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/mutex.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
#include <linux/input.h>
#include <linux/backlight.h>
#include <linux/thermal.h>
@@ -152,9 +150,6 @@ struct acpi_video_bus {
struct acpi_video_bus_flags flags;
struct list_head video_device_list;
struct mutex device_list_lock; /* protects video_device_list */
-#ifdef CONFIG_ACPI_PROCFS
- struct proc_dir_entry *dir;
-#endif
struct input_dev *input;
char phys[32]; /* for input device */
struct notifier_block pm_nb;
@@ -210,108 +205,6 @@ struct acpi_video_device {
struct output_device *output_dev;
};
-#ifdef CONFIG_ACPI_PROCFS
-/* bus */
-static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
-static const struct file_operations acpi_video_bus_info_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_bus_info_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
-static const struct file_operations acpi_video_bus_ROM_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_bus_ROM_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
- struct file *file);
-static const struct file_operations acpi_video_bus_POST_info_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_bus_POST_info_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
-static ssize_t acpi_video_bus_write_POST(struct file *file,
- const char __user *buffer, size_t count, loff_t *data);
-static const struct file_operations acpi_video_bus_POST_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_bus_POST_open_fs,
- .read = seq_read,
- .write = acpi_video_bus_write_POST,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
-static ssize_t acpi_video_bus_write_DOS(struct file *file,
- const char __user *buffer, size_t count, loff_t *data);
-static const struct file_operations acpi_video_bus_DOS_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_bus_DOS_open_fs,
- .read = seq_read,
- .write = acpi_video_bus_write_DOS,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-/* device */
-static int acpi_video_device_info_open_fs(struct inode *inode,
- struct file *file);
-static const struct file_operations acpi_video_device_info_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_device_info_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int acpi_video_device_state_open_fs(struct inode *inode,
- struct file *file);
-static ssize_t acpi_video_device_write_state(struct file *file,
- const char __user *buffer, size_t count, loff_t *data);
-static const struct file_operations acpi_video_device_state_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_device_state_open_fs,
- .read = seq_read,
- .write = acpi_video_device_write_state,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int acpi_video_device_brightness_open_fs(struct inode *inode,
- struct file *file);
-static ssize_t acpi_video_device_write_brightness(struct file *file,
- const char __user *buffer, size_t count, loff_t *data);
-static const struct file_operations acpi_video_device_brightness_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_device_brightness_open_fs,
- .read = seq_read,
- .write = acpi_video_device_write_brightness,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int acpi_video_device_EDID_open_fs(struct inode *inode,
- struct file *file);
-static const struct file_operations acpi_video_device_EDID_fops = {
- .owner = THIS_MODULE,
- .open = acpi_video_device_EDID_open_fs,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif /* CONFIG_ACPI_PROCFS */
-
static const char device_decode[][30] = {
"motherboard VGA device",
"PCI VGA device",
@@ -1111,646 +1004,6 @@ static int acpi_video_bus_check(struct acpi_video_bus *video)
}
/* --------------------------------------------------------------------------
- FS Interface (/proc)
- -------------------------------------------------------------------------- */
-#ifdef CONFIG_ACPI_PROCFS
-
-static struct proc_dir_entry *acpi_video_dir;
-
-/* video devices */
-
-static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_device *dev = seq->private;
-
-
- if (!dev)
- goto end;
-
- seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id);
- seq_printf(seq, "type: ");
- if (dev->flags.crt)
- seq_printf(seq, "CRT\n");
- else if (dev->flags.lcd)
- seq_printf(seq, "LCD\n");
- else if (dev->flags.tvout)
- seq_printf(seq, "TVOUT\n");
- else if (dev->flags.dvi)
- seq_printf(seq, "DVI\n");
- else
- seq_printf(seq, "UNKNOWN\n");
-
- seq_printf(seq, "known by bios: %s\n", dev->flags.bios ? "yes" : "no");
-
- end:
- return 0;
-}
-
-static int
-acpi_video_device_info_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_device_info_seq_show,
- PDE(inode)->data);
-}
-
-static int
-acpi_video_device_query(struct acpi_video_device *device,
- unsigned long long *state)
-{
- int status;
-
- status = acpi_evaluate_integer(device->dev->handle, "_DGS",
- NULL, state);
-
- return status;
-}
-
-static int acpi_video_device_state_seq_show(struct seq_file *seq, void *offset)
-{
- int status;
- struct acpi_video_device *dev = seq->private;
- unsigned long long state;
-
-
- if (!dev)
- goto end;
-
- status = acpi_video_device_get_state(dev, &state);
- seq_printf(seq, "state: ");
- if (ACPI_SUCCESS(status))
- seq_printf(seq, "0x%02llx\n", state);
- else
- seq_printf(seq, "<not supported>\n");
-
- status = acpi_video_device_query(dev, &state);
- seq_printf(seq, "query: ");
- if (ACPI_SUCCESS(status))
- seq_printf(seq, "0x%02llx\n", state);
- else
- seq_printf(seq, "<not supported>\n");
-
- end:
- return 0;
-}
-
-static int
-acpi_video_device_state_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_device_state_seq_show,
- PDE(inode)->data);
-}
-
-static ssize_t
-acpi_video_device_write_state(struct file *file,
- const char __user * buffer,
- size_t count, loff_t * data)
-{
- int status;
- struct seq_file *m = file->private_data;
- struct acpi_video_device *dev = m->private;
- char str[12] = { 0 };
- u32 state = 0;
-
-
- if (!dev || count >= sizeof(str))
- return -EINVAL;
-
- if (copy_from_user(str, buffer, count))
- return -EFAULT;
-
- str[count] = 0;
- state = simple_strtoul(str, NULL, 0);
- state &= ((1ul << 31) | (1ul << 30) | (1ul << 0));
-
- status = acpi_video_device_set_state(dev, state);
-
- if (status)
- return -EFAULT;
-
- return count;
-}
-
-static int
-acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_device *dev = seq->private;
- int i;
-
-
- if (!dev || !dev->brightness) {
- seq_printf(seq, "<not supported>\n");
- return 0;
- }
-
- seq_printf(seq, "levels: ");
- for (i = 2; i < dev->brightness->count; i++)
- seq_printf(seq, " %d", dev->brightness->levels[i]);
- seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
-
- return 0;
-}
-
-static int
-acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_device_brightness_seq_show,
- PDE(inode)->data);
-}
-
-static ssize_t
-acpi_video_device_write_brightness(struct file *file,
- const char __user * buffer,
- size_t count, loff_t * data)
-{
- struct seq_file *m = file->private_data;
- struct acpi_video_device *dev = m->private;
- char str[5] = { 0 };
- unsigned int level = 0;
- int i;
-
-
- if (!dev || !dev->brightness || count >= sizeof(str))
- return -EINVAL;
-
- if (copy_from_user(str, buffer, count))
- return -EFAULT;
-
- str[count] = 0;
- level = simple_strtoul(str, NULL, 0);
-
- if (level > 100)
- return -EFAULT;
-
- /* validate through the list of available levels */
- for (i = 2; i < dev->brightness->count; i++)
- if (level == dev->brightness->levels[i]) {
- if (!acpi_video_device_lcd_set_level(dev, level))
- return count;
- break;
- }
-
- return -EINVAL;
-}
-
-static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_device *dev = seq->private;
- int status;
- int i;
- union acpi_object *edid = NULL;
-
-
- if (!dev)
- goto out;
-
- status = acpi_video_device_EDID(dev, &edid, 128);
- if (ACPI_FAILURE(status)) {
- status = acpi_video_device_EDID(dev, &edid, 256);
- }
-
- if (ACPI_FAILURE(status)) {
- goto out;
- }
-
- if (edid && edid->type == ACPI_TYPE_BUFFER) {
- for (i = 0; i < edid->buffer.length; i++)
- seq_putc(seq, edid->buffer.pointer[i]);
- }
-
- out:
- if (!edid)
- seq_printf(seq, "<not supported>\n");
- else
- kfree(edid);
-
- return 0;
-}
-
-static int
-acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_device_EDID_seq_show,
- PDE(inode)->data);
-}
-
-static int acpi_video_device_add_fs(struct acpi_device *device)
-{
- struct proc_dir_entry *entry, *device_dir;
- struct acpi_video_device *vid_dev;
-
- vid_dev = acpi_driver_data(device);
- if (!vid_dev)
- return -ENODEV;
-
- device_dir = proc_mkdir(acpi_device_bid(device),
- vid_dev->video->dir);
- if (!device_dir)
- return -ENOMEM;
-
- /* 'info' [R] */
- entry = proc_create_data("info", S_IRUGO, device_dir,
- &acpi_video_device_info_fops, acpi_driver_data(device));
- if (!entry)
- goto err_remove_dir;
-
- /* 'state' [R/W] */
- entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
- device_dir,
- &acpi_video_device_state_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_info;
-
- /* 'brightness' [R/W] */
- entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
- device_dir,
- &acpi_video_device_brightness_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_state;
-
- /* 'EDID' [R] */
- entry = proc_create_data("EDID", S_IRUGO, device_dir,
- &acpi_video_device_EDID_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_brightness;
-
- acpi_device_dir(device) = device_dir;
-
- return 0;
-
- err_remove_brightness:
- remove_proc_entry("brightness", device_dir);
- err_remove_state:
- remove_proc_entry("state", device_dir);
- err_remove_info:
- remove_proc_entry("info", device_dir);
- err_remove_dir:
- remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
- return -ENOMEM;
-}
-
-static int acpi_video_device_remove_fs(struct acpi_device *device)
-{
- struct acpi_video_device *vid_dev;
- struct proc_dir_entry *device_dir;
-
- vid_dev = acpi_driver_data(device);
- if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
- return -ENODEV;
-
- device_dir = acpi_device_dir(device);
- if (device_dir) {
- remove_proc_entry("info", device_dir);
- remove_proc_entry("state", device_dir);
- remove_proc_entry("brightness", device_dir);
- remove_proc_entry("EDID", device_dir);
- remove_proc_entry(acpi_device_bid(device), vid_dev->video->dir);
- acpi_device_dir(device) = NULL;
- }
-
- return 0;
-}
-
-/* video bus */
-static int acpi_video_bus_info_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_bus *video = seq->private;
-
-
- if (!video)
- goto end;
-
- seq_printf(seq, "Switching heads: %s\n",
- video->flags.multihead ? "yes" : "no");
- seq_printf(seq, "Video ROM: %s\n",
- video->flags.rom ? "yes" : "no");
- seq_printf(seq, "Device to be POSTed on boot: %s\n",
- video->flags.post ? "yes" : "no");
-
- end:
- return 0;
-}
-
-static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_bus_info_seq_show,
- PDE(inode)->data);
-}
-
-static int acpi_video_bus_ROM_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_bus *video = seq->private;
-
-
- if (!video)
- goto end;
-
- printk(KERN_INFO PREFIX "Please implement %s\n", __func__);
- seq_printf(seq, "<TODO>\n");
-
- end:
- return 0;
-}
-
-static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
-}
-
-static int
-acpi_video_bus_POST_options(struct acpi_video_bus *video,
- unsigned long long *options)
-{
- int status;
-
- status = acpi_evaluate_integer(video->device->handle, "_VPO",
- NULL, options);
- *options &= 3;
-
- return status;
-}
-
-static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_bus *video = seq->private;
- unsigned long long options;
- int status;
-
-
- if (!video)
- goto end;
-
- status = acpi_video_bus_POST_options(video, &options);
- if (ACPI_SUCCESS(status)) {
- if (!(options & 1)) {
- printk(KERN_WARNING PREFIX
- "The motherboard VGA device is not listed as a possible POST device.\n");
- printk(KERN_WARNING PREFIX
- "This indicates a BIOS bug. Please contact the manufacturer.\n");
- }
- printk(KERN_WARNING "%llx\n", options);
- seq_printf(seq, "can POST: <integrated video>");
- if (options & 2)
- seq_printf(seq, " <PCI video>");
- if (options & 4)
- seq_printf(seq, " <AGP video>");
- seq_putc(seq, '\n');
- } else
- seq_printf(seq, "<not supported>\n");
- end:
- return 0;
-}
-
-static int
-acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_bus_POST_info_seq_show,
- PDE(inode)->data);
-}
-
-static int
-acpi_video_bus_get_POST(struct acpi_video_bus *video, unsigned long long *id)
-{
- int status;
-
- status = acpi_evaluate_integer(video->device->handle, "_GPD", NULL, id);
-
- return status;
-}
-
-static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_bus *video = seq->private;
- int status;
- unsigned long long id;
-
-
- if (!video)
- goto end;
-
- status = acpi_video_bus_get_POST(video, &id);
- if (!ACPI_SUCCESS(status)) {
- seq_printf(seq, "<not supported>\n");
- goto end;
- }
- seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]);
-
- end:
- return 0;
-}
-
-static int acpi_video_bus_DOS_seq_show(struct seq_file *seq, void *offset)
-{
- struct acpi_video_bus *video = seq->private;
-
-
- seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting);
-
- return 0;
-}
-
-static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_bus_POST_seq_show,
- PDE(inode)->data);
-}
-
-static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file)
-{
- return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
-}
-
-static int
-acpi_video_bus_set_POST(struct acpi_video_bus *video, unsigned long option)
-{
- int status;
- unsigned long long tmp;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
-
-
- arg0.integer.value = option;
-
- status = acpi_evaluate_integer(video->device->handle, "_SPD",
- &args, &tmp);
- if (ACPI_SUCCESS(status))
- status = tmp ? (-EINVAL) : (AE_OK);
-
- return status;
-}
-
-static ssize_t
-acpi_video_bus_write_POST(struct file *file,
- const char __user * buffer,
- size_t count, loff_t * data)
-{
- int status;
- struct seq_file *m = file->private_data;
- struct acpi_video_bus *video = m->private;
- char str[12] = { 0 };
- unsigned long long opt, options;
-
-
- if (!video || count >= sizeof(str))
- return -EINVAL;
-
- status = acpi_video_bus_POST_options(video, &options);
- if (!ACPI_SUCCESS(status))
- return -EINVAL;
-
- if (copy_from_user(str, buffer, count))
- return -EFAULT;
-
- str[count] = 0;
- opt = strtoul(str, NULL, 0);
- if (opt > 3)
- return -EFAULT;
-
- /* just in case an OEM 'forgot' the motherboard... */
- options |= 1;
-
- if (options & (1ul << opt)) {
- status = acpi_video_bus_set_POST(video, opt);
- if (!ACPI_SUCCESS(status))
- return -EFAULT;
-
- }
-
- return count;
-}
-
-static ssize_t
-acpi_video_bus_write_DOS(struct file *file,
- const char __user * buffer,
- size_t count, loff_t * data)
-{
- int status;
- struct seq_file *m = file->private_data;
- struct acpi_video_bus *video = m->private;
- char str[12] = { 0 };
- unsigned long opt;
-
-
- if (!video || count >= sizeof(str))
- return -EINVAL;
-
- if (copy_from_user(str, buffer, count))
- return -EFAULT;
-
- str[count] = 0;
- opt = strtoul(str, NULL, 0);
- if (opt > 7)
- return -EFAULT;
-
- status = acpi_video_bus_DOS(video, opt & 0x3, (opt & 0x4) >> 2);
-
- if (!ACPI_SUCCESS(status))
- return -EFAULT;
-
- return count;
-}
-
-static int acpi_video_bus_add_fs(struct acpi_device *device)
-{
- struct acpi_video_bus *video = acpi_driver_data(device);
- struct proc_dir_entry *device_dir;
- struct proc_dir_entry *entry;
-
- device_dir = proc_mkdir(acpi_device_bid(device), acpi_video_dir);
- if (!device_dir)
- return -ENOMEM;
-
- /* 'info' [R] */
- entry = proc_create_data("info", S_IRUGO, device_dir,
- &acpi_video_bus_info_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_dir;
-
- /* 'ROM' [R] */
- entry = proc_create_data("ROM", S_IRUGO, device_dir,
- &acpi_video_bus_ROM_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_info;
-
- /* 'POST_info' [R] */
- entry = proc_create_data("POST_info", S_IRUGO, device_dir,
- &acpi_video_bus_POST_info_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_rom;
-
- /* 'POST' [R/W] */
- entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
- device_dir,
- &acpi_video_bus_POST_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_post_info;
-
- /* 'DOS' [R/W] */
- entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
- device_dir,
- &acpi_video_bus_DOS_fops,
- acpi_driver_data(device));
- if (!entry)
- goto err_remove_post;
-
- video->dir = acpi_device_dir(device) = device_dir;
- return 0;
-
- err_remove_post:
- remove_proc_entry("POST", device_dir);
- err_remove_post_info:
- remove_proc_entry("POST_info", device_dir);
- err_remove_rom:
- remove_proc_entry("ROM", device_dir);
- err_remove_info:
- remove_proc_entry("info", device_dir);
- err_remove_dir:
- remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
- return -ENOMEM;
-}
-
-static int acpi_video_bus_remove_fs(struct acpi_device *device)
-{
- struct proc_dir_entry *device_dir = acpi_device_dir(device);
-
- if (device_dir) {
- remove_proc_entry("info", device_dir);
- remove_proc_entry("ROM", device_dir);
- remove_proc_entry("POST_info", device_dir);
- remove_proc_entry("POST", device_dir);
- remove_proc_entry("DOS", device_dir);
- remove_proc_entry(acpi_device_bid(device), acpi_video_dir);
- acpi_device_dir(device) = NULL;
- }
-
- return 0;
-}
-#else
-static inline int acpi_video_device_add_fs(struct acpi_device *device)
-{
- return 0;
-}
-static inline int acpi_video_device_remove_fs(struct acpi_device *device)
-{
- return 0;
-}
-static inline int acpi_video_bus_add_fs(struct acpi_device *device)
-{
- return 0;
-}
-static inline int acpi_video_bus_remove_fs(struct acpi_device *device)
-{
- return 0;
-}
-#endif /* CONFIG_ACPI_PROCFS */
-
-/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
@@ -1877,8 +1130,6 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
list_add_tail(&data->entry, &video->video_device_list);
mutex_unlock(&video->device_list_lock);
- acpi_video_device_add_fs(device);
-
return 0;
}
@@ -2181,8 +1432,6 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
if (!device || !device->video)
return -ENOENT;
- acpi_video_device_remove_fs(device->dev);
-
status = acpi_remove_notify_handler(device->dev->handle,
ACPI_DEVICE_NOTIFY,
acpi_video_device_notify);
@@ -2466,10 +1715,6 @@ static int acpi_video_bus_add(struct acpi_device *device)
if (error)
goto err_free_video;
- error = acpi_video_bus_add_fs(device);
- if (error)
- goto err_free_video;
-
mutex_init(&video->device_list_lock);
INIT_LIST_HEAD(&video->video_device_list);
@@ -2522,7 +1767,6 @@ static int acpi_video_bus_add(struct acpi_device *device)
acpi_video_bus_stop_devices(video);
acpi_video_bus_put_devices(video);
kfree(video->attached_array);
- acpi_video_bus_remove_fs(device);
err_free_video:
kfree(video);
device->driver_data = NULL;
@@ -2544,7 +1788,6 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
acpi_video_bus_stop_devices(video);
acpi_video_bus_put_devices(video);
- acpi_video_bus_remove_fs(device);
input_unregister_device(video->input);
kfree(video->attached_array);
@@ -2584,17 +1827,9 @@ int acpi_video_register(void)
return 0;
}
-#ifdef CONFIG_ACPI_PROCFS
- acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
- if (!acpi_video_dir)
- return -ENODEV;
-#endif
-
result = acpi_bus_register_driver(&acpi_video_bus);
- if (result < 0) {
- remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+ if (result < 0)
return -ENODEV;
- }
/*
* When the acpi_video_bus is loaded successfully, increase
@@ -2617,10 +1852,6 @@ void acpi_video_unregister(void)
}
acpi_bus_unregister_driver(&acpi_video_bus);
-#ifdef CONFIG_ACPI_PROCFS
- remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
-#endif
-
register_count = 0;
return;
diff --git a/drivers/base/node.c b/drivers/base/node.c
index ee53558b452f..ce012a9c6201 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -160,6 +160,18 @@ static ssize_t node_read_numastat(struct sys_device * dev,
}
static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL);
+static ssize_t node_read_vmstat(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ int nid = dev->id;
+ return sprintf(buf,
+ "nr_written %lu\n"
+ "nr_dirtied %lu\n",
+ node_page_state(nid, NR_WRITTEN),
+ node_page_state(nid, NR_DIRTIED));
+}
+static SYSDEV_ATTR(vmstat, S_IRUGO, node_read_vmstat, NULL);
+
static ssize_t node_read_distance(struct sys_device * dev,
struct sysdev_attribute *attr, char * buf)
{
@@ -243,6 +255,7 @@ int register_node(struct node *node, int num, struct node *parent)
sysdev_create_file(&node->sysdev, &attr_meminfo);
sysdev_create_file(&node->sysdev, &attr_numastat);
sysdev_create_file(&node->sysdev, &attr_distance);
+ sysdev_create_file(&node->sysdev, &attr_vmstat);
scan_unevictable_register_node(node);
@@ -267,6 +280,7 @@ void unregister_node(struct node *node)
sysdev_remove_file(&node->sysdev, &attr_meminfo);
sysdev_remove_file(&node->sysdev, &attr_numastat);
sysdev_remove_file(&node->sysdev, &attr_distance);
+ sysdev_remove_file(&node->sysdev, &attr_vmstat);
scan_unevictable_unregister_node(node);
hugetlb_unregister_node(node); /* no-op, if memoryless node */
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 3966e62ad019..f051cfff18af 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -147,6 +147,7 @@ static void platform_device_release(struct device *dev)
struct platform_object *pa = container_of(dev, struct platform_object,
pdev.dev);
+ of_device_node_put(&pa->pdev.dev);
kfree(pa->pdev.dev.platform_data);
kfree(pa->pdev.resource);
kfree(pa);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 1dd8676d7f55..126ca492dd08 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -503,7 +503,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
* the resume will actually succeed.
*/
if (dev->power.no_callbacks && !parent && dev->parent) {
- spin_lock(&dev->parent->power.lock);
+ spin_lock_nested(&dev->parent->power.lock, SINGLE_DEPTH_NESTING);
if (dev->parent->power.disable_depth > 0
|| dev->parent->power.ignore_children
|| dev->parent->power.runtime_status == RPM_ACTIVE) {
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 6c48b3545f84..450c958b514f 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -101,8 +101,8 @@ static int transfer_none(struct loop_device *lo, int cmd,
else
memcpy(raw_buf, loop_buf, size);
- kunmap_atomic(raw_buf, KM_USER0);
kunmap_atomic(loop_buf, KM_USER1);
+ kunmap_atomic(raw_buf, KM_USER0);
cond_resched();
return 0;
}
@@ -130,8 +130,8 @@ static int transfer_xor(struct loop_device *lo, int cmd,
for (i = 0; i < size; i++)
*out++ = *in++ ^ key[(i & 511) % keysize];
- kunmap_atomic(raw_buf, KM_USER0);
kunmap_atomic(loop_buf, KM_USER1);
+ kunmap_atomic(raw_buf, KM_USER0);
cond_resched();
return 0;
}
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index 6e968cd4893c..829161edae53 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -1225,7 +1225,8 @@ ace_of_probe(struct platform_device *op, const struct of_device_id *match)
bus_width = ACE_BUS_WIDTH_8;
/* Call the bus-independant setup code */
- return ace_alloc(&op->dev, id ? *id : 0, physaddr, irq, bus_width);
+ return ace_alloc(&op->dev, id ? be32_to_cpup(id) : 0,
+ physaddr, irq, bus_width);
}
static int __devexit ace_of_remove(struct platform_device *op)
diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile
index 627f542827c7..8eb56e273e75 100644
--- a/drivers/char/agp/Makefile
+++ b/drivers/char/agp/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o
obj-$(CONFIG_AGP_PARISC) += parisc-agp.o
obj-$(CONFIG_AGP_I460) += i460-agp.o
obj-$(CONFIG_AGP_INTEL) += intel-agp.o
+obj-$(CONFIG_AGP_INTEL) += intel-gtt.o
obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o
obj-$(CONFIG_AGP_SGI_TIOCA) += sgi-agp.o
obj-$(CONFIG_AGP_SIS) += sis-agp.o
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
index 120490949997..5259065f3c79 100644
--- a/drivers/char/agp/agp.h
+++ b/drivers/char/agp/agp.h
@@ -121,11 +121,6 @@ struct agp_bridge_driver {
void (*agp_destroy_pages)(struct agp_memory *);
int (*agp_type_to_mask_type) (struct agp_bridge_data *, int);
void (*chipset_flush)(struct agp_bridge_data *);
-
- int (*agp_map_page)(struct page *page, dma_addr_t *ret);
- void (*agp_unmap_page)(struct page *page, dma_addr_t dma);
- int (*agp_map_memory)(struct agp_memory *mem);
- void (*agp_unmap_memory)(struct agp_memory *mem);
};
struct agp_bridge_data {
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
index b6b1568314c8..b1b4362bc648 100644
--- a/drivers/char/agp/amd-k7-agp.c
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -309,7 +309,8 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
num_entries = A_SIZE_LVL2(agp_bridge->current_size)->num_entries;
- if (type != 0 || mem->type != 0)
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type))
return -EINVAL;
if ((pg_start + mem->page_count) > num_entries)
@@ -348,7 +349,8 @@ static int amd_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
unsigned long __iomem *cur_gatt;
unsigned long addr;
- if (type != 0 || mem->type != 0)
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type))
return -EINVAL;
for (i = pg_start; i < (mem->page_count + pg_start); i++) {
diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c
index ee4f855611b6..f27d0d0816d3 100644
--- a/drivers/char/agp/backend.c
+++ b/drivers/char/agp/backend.c
@@ -151,17 +151,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge)
}
bridge->scratch_page_page = page;
- if (bridge->driver->agp_map_page) {
- if (bridge->driver->agp_map_page(page,
- &bridge->scratch_page_dma)) {
- dev_err(&bridge->dev->dev,
- "unable to dma-map scratch page\n");
- rc = -ENOMEM;
- goto err_out_nounmap;
- }
- } else {
- bridge->scratch_page_dma = page_to_phys(page);
- }
+ bridge->scratch_page_dma = page_to_phys(page);
bridge->scratch_page = bridge->driver->mask_memory(bridge,
bridge->scratch_page_dma, 0);
@@ -204,12 +194,6 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge)
return 0;
err_out:
- if (bridge->driver->needs_scratch_page &&
- bridge->driver->agp_unmap_page) {
- bridge->driver->agp_unmap_page(bridge->scratch_page_page,
- bridge->scratch_page_dma);
- }
-err_out_nounmap:
if (bridge->driver->needs_scratch_page) {
void *va = page_address(bridge->scratch_page_page);
@@ -240,10 +224,6 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge)
bridge->driver->needs_scratch_page) {
void *va = page_address(bridge->scratch_page_page);
- if (bridge->driver->agp_unmap_page)
- bridge->driver->agp_unmap_page(bridge->scratch_page_page,
- bridge->scratch_page_dma);
-
bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP);
bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE);
}
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 64255cef8a7d..4956f1c8f9d5 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -437,11 +437,6 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start)
curr->is_flushed = true;
}
- if (curr->bridge->driver->agp_map_memory) {
- ret_val = curr->bridge->driver->agp_map_memory(curr);
- if (ret_val)
- return ret_val;
- }
ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type);
if (ret_val != 0)
@@ -483,9 +478,6 @@ int agp_unbind_memory(struct agp_memory *curr)
if (ret_val != 0)
return ret_val;
- if (curr->bridge->driver->agp_unmap_memory)
- curr->bridge->driver->agp_unmap_memory(curr);
-
curr->is_bound = false;
curr->pg_start = 0;
spin_lock(&curr->bridge->mapped_lock);
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index cd18493c9527..e72f49d52202 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -12,9 +12,6 @@
#include <asm/smp.h>
#include "agp.h"
#include "intel-agp.h"
-#include <linux/intel-gtt.h>
-
-#include "intel-gtt.c"
int intel_agp_enabled;
EXPORT_SYMBOL(intel_agp_enabled);
@@ -703,179 +700,37 @@ static const struct agp_bridge_driver intel_7505_driver = {
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
};
-static int find_gmch(u16 device)
-{
- struct pci_dev *gmch_device;
-
- gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL);
- if (gmch_device && PCI_FUNC(gmch_device->devfn) != 0) {
- gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL,
- device, gmch_device);
- }
-
- if (!gmch_device)
- return 0;
-
- intel_private.pcidev = gmch_device;
- return 1;
-}
-
/* Table to describe Intel GMCH and AGP/PCIE GART drivers. At least one of
* driver and gmch_driver must be non-null, and find_gmch will determine
* which one should be used if a gmch_chip_id is present.
*/
-static const struct intel_driver_description {
+static const struct intel_agp_driver_description {
unsigned int chip_id;
- unsigned int gmch_chip_id;
char *name;
const struct agp_bridge_driver *driver;
- const struct agp_bridge_driver *gmch_driver;
} intel_agp_chipsets[] = {
- { PCI_DEVICE_ID_INTEL_82443LX_0, 0, "440LX", &intel_generic_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82443BX_0, 0, "440BX", &intel_generic_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82443GX_0, 0, "440GX", &intel_generic_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82810_MC1, PCI_DEVICE_ID_INTEL_82810_IG1, "i810",
- NULL, &intel_810_driver },
- { PCI_DEVICE_ID_INTEL_82810_MC3, PCI_DEVICE_ID_INTEL_82810_IG3, "i810",
- NULL, &intel_810_driver },
- { PCI_DEVICE_ID_INTEL_82810E_MC, PCI_DEVICE_ID_INTEL_82810E_IG, "i810",
- NULL, &intel_810_driver },
- { PCI_DEVICE_ID_INTEL_82815_MC, PCI_DEVICE_ID_INTEL_82815_CGC, "i815",
- &intel_815_driver, &intel_810_driver },
- { PCI_DEVICE_ID_INTEL_82820_HB, 0, "i820", &intel_820_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82820_UP_HB, 0, "i820", &intel_820_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82830_HB, PCI_DEVICE_ID_INTEL_82830_CGC, "830M",
- &intel_830mp_driver, &intel_830_driver },
- { PCI_DEVICE_ID_INTEL_82840_HB, 0, "i840", &intel_840_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82845_HB, 0, "845G", &intel_845_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82845G_HB, PCI_DEVICE_ID_INTEL_82845G_IG, "830M",
- &intel_845_driver, &intel_830_driver },
- { PCI_DEVICE_ID_INTEL_82850_HB, 0, "i850", &intel_850_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82854_HB, PCI_DEVICE_ID_INTEL_82854_IG, "854",
- &intel_845_driver, &intel_830_driver },
- { PCI_DEVICE_ID_INTEL_82855PM_HB, 0, "855PM", &intel_845_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82855GM_HB, PCI_DEVICE_ID_INTEL_82855GM_IG, "855GM",
- &intel_845_driver, &intel_830_driver },
- { PCI_DEVICE_ID_INTEL_82860_HB, 0, "i860", &intel_860_driver, NULL },
- { PCI_DEVICE_ID_INTEL_82865_HB, PCI_DEVICE_ID_INTEL_82865_IG, "865",
- &intel_845_driver, &intel_830_driver },
- { PCI_DEVICE_ID_INTEL_82875_HB, 0, "i875", &intel_845_driver, NULL },
- { PCI_DEVICE_ID_INTEL_E7221_HB, PCI_DEVICE_ID_INTEL_E7221_IG, "E7221 (i915)",
- NULL, &intel_915_driver },
- { PCI_DEVICE_ID_INTEL_82915G_HB, PCI_DEVICE_ID_INTEL_82915G_IG, "915G",
- NULL, &intel_915_driver },
- { PCI_DEVICE_ID_INTEL_82915GM_HB, PCI_DEVICE_ID_INTEL_82915GM_IG, "915GM",
- NULL, &intel_915_driver },
- { PCI_DEVICE_ID_INTEL_82945G_HB, PCI_DEVICE_ID_INTEL_82945G_IG, "945G",
- NULL, &intel_915_driver },
- { PCI_DEVICE_ID_INTEL_82945GM_HB, PCI_DEVICE_ID_INTEL_82945GM_IG, "945GM",
- NULL, &intel_915_driver },
- { PCI_DEVICE_ID_INTEL_82945GME_HB, PCI_DEVICE_ID_INTEL_82945GME_IG, "945GME",
- NULL, &intel_915_driver },
- { PCI_DEVICE_ID_INTEL_82946GZ_HB, PCI_DEVICE_ID_INTEL_82946GZ_IG, "946GZ",
- NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_82G35_HB, PCI_DEVICE_ID_INTEL_82G35_IG, "G35",
- NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_82965Q_HB, PCI_DEVICE_ID_INTEL_82965Q_IG, "965Q",
- NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_82965G_HB, PCI_DEVICE_ID_INTEL_82965G_IG, "965G",
- NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_82965GM_HB, PCI_DEVICE_ID_INTEL_82965GM_IG, "965GM",
- NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_82965GME_HB, PCI_DEVICE_ID_INTEL_82965GME_IG, "965GME/GLE",
- NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_7505_0, 0, "E7505", &intel_7505_driver, NULL },
- { PCI_DEVICE_ID_INTEL_7205_0, 0, "E7205", &intel_7505_driver, NULL },
- { PCI_DEVICE_ID_INTEL_G33_HB, PCI_DEVICE_ID_INTEL_G33_IG, "G33",
- NULL, &intel_g33_driver },
- { PCI_DEVICE_ID_INTEL_Q35_HB, PCI_DEVICE_ID_INTEL_Q35_IG, "Q35",
- NULL, &intel_g33_driver },
- { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, "Q33",
- NULL, &intel_g33_driver },
- { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, "GMA3150",
- NULL, &intel_g33_driver },
- { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, "GMA3150",
- NULL, &intel_g33_driver },
- { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG,
- "GM45", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_EAGLELAKE_HB, PCI_DEVICE_ID_INTEL_EAGLELAKE_IG,
- "Eaglelake", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG,
- "Q45/Q43", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG,
- "G45/G43", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG,
- "B43", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_B43_1_HB, PCI_DEVICE_ID_INTEL_B43_1_IG,
- "B43", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG,
- "G41", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG,
- "HD Graphics", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG,
- "HD Graphics", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG,
- "HD Graphics", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG,
- "HD Graphics", NULL, &intel_i965_driver },
- { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG,
- "Sandybridge", NULL, &intel_gen6_driver },
- { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG,
- "Sandybridge", NULL, &intel_gen6_driver },
- { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG,
- "Sandybridge", NULL, &intel_gen6_driver },
- { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG,
- "Sandybridge", NULL, &intel_gen6_driver },
- { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG,
- "Sandybridge", NULL, &intel_gen6_driver },
- { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG,
- "Sandybridge", NULL, &intel_gen6_driver },
- { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG,
- "Sandybridge", NULL, &intel_gen6_driver },
- { 0, 0, NULL, NULL, NULL }
+ { PCI_DEVICE_ID_INTEL_82443LX_0, "440LX", &intel_generic_driver },
+ { PCI_DEVICE_ID_INTEL_82443BX_0, "440BX", &intel_generic_driver },
+ { PCI_DEVICE_ID_INTEL_82443GX_0, "440GX", &intel_generic_driver },
+ { PCI_DEVICE_ID_INTEL_82815_MC, "i815", &intel_815_driver },
+ { PCI_DEVICE_ID_INTEL_82820_HB, "i820", &intel_820_driver },
+ { PCI_DEVICE_ID_INTEL_82820_UP_HB, "i820", &intel_820_driver },
+ { PCI_DEVICE_ID_INTEL_82830_HB, "830M", &intel_830mp_driver },
+ { PCI_DEVICE_ID_INTEL_82840_HB, "i840", &intel_840_driver },
+ { PCI_DEVICE_ID_INTEL_82845_HB, "845G", &intel_845_driver },
+ { PCI_DEVICE_ID_INTEL_82845G_HB, "830M", &intel_845_driver },
+ { PCI_DEVICE_ID_INTEL_82850_HB, "i850", &intel_850_driver },
+ { PCI_DEVICE_ID_INTEL_82854_HB, "854", &intel_845_driver },
+ { PCI_DEVICE_ID_INTEL_82855PM_HB, "855PM", &intel_845_driver },
+ { PCI_DEVICE_ID_INTEL_82855GM_HB, "855GM", &intel_845_driver },
+ { PCI_DEVICE_ID_INTEL_82860_HB, "i860", &intel_860_driver },
+ { PCI_DEVICE_ID_INTEL_82865_HB, "865", &intel_845_driver },
+ { PCI_DEVICE_ID_INTEL_82875_HB, "i875", &intel_845_driver },
+ { PCI_DEVICE_ID_INTEL_7505_0, "E7505", &intel_7505_driver },
+ { PCI_DEVICE_ID_INTEL_7205_0, "E7205", &intel_7505_driver },
+ { 0, NULL, NULL }
};
-static int __devinit intel_gmch_probe(struct pci_dev *pdev,
- struct agp_bridge_data *bridge)
-{
- int i, mask;
-
- bridge->driver = NULL;
-
- for (i = 0; intel_agp_chipsets[i].name != NULL; i++) {
- if ((intel_agp_chipsets[i].gmch_chip_id != 0) &&
- find_gmch(intel_agp_chipsets[i].gmch_chip_id)) {
- bridge->driver =
- intel_agp_chipsets[i].gmch_driver;
- break;
- }
- }
-
- if (!bridge->driver)
- return 0;
-
- bridge->dev_private_data = &intel_private;
- bridge->dev = pdev;
-
- dev_info(&pdev->dev, "Intel %s Chipset\n", intel_agp_chipsets[i].name);
-
- if (bridge->driver->mask_memory == intel_gen6_mask_memory)
- mask = 40;
- else if (bridge->driver->mask_memory == intel_i965_mask_memory)
- mask = 36;
- else
- mask = 32;
-
- if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask)))
- dev_err(&intel_private.pcidev->dev,
- "set gfx device dma mask %d-bit failed!\n", mask);
- else
- pci_set_consistent_dma_mask(intel_private.pcidev,
- DMA_BIT_MASK(mask));
-
- return 1;
-}
-
static int __devinit agp_intel_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -905,7 +760,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev,
}
}
- if (intel_agp_chipsets[i].name == NULL) {
+ if (!bridge->driver) {
if (cap_ptr)
dev_warn(&pdev->dev, "unsupported Intel chipset [%04x/%04x]\n",
pdev->vendor, pdev->device);
@@ -913,14 +768,6 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev,
return -ENODEV;
}
- if (!bridge->driver) {
- if (cap_ptr)
- dev_warn(&pdev->dev, "can't find bridge device (chip_id: %04x)\n",
- intel_agp_chipsets[i].gmch_chip_id);
- agp_put_bridge(bridge);
- return -ENODEV;
- }
-
bridge->dev = pdev;
bridge->dev_private_data = NULL;
@@ -972,8 +819,7 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev)
agp_remove_bridge(bridge);
- if (intel_private.pcidev)
- pci_dev_put(intel_private.pcidev);
+ intel_gmch_remove(pdev);
agp_put_bridge(bridge);
}
@@ -1049,6 +895,7 @@ static struct pci_device_id agp_intel_pci_table[] = {
ID(PCI_DEVICE_ID_INTEL_G45_HB),
ID(PCI_DEVICE_ID_INTEL_G41_HB),
ID(PCI_DEVICE_ID_INTEL_B43_HB),
+ ID(PCI_DEVICE_ID_INTEL_B43_1_HB),
ID(PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB),
ID(PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB),
ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB),
diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h
index d09b1ab7e8ab..90539df02504 100644
--- a/drivers/char/agp/intel-agp.h
+++ b/drivers/char/agp/intel-agp.h
@@ -215,44 +215,7 @@
#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB 0x0108 /* Server */
#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG 0x010A
-/* cover 915 and 945 variants */
-#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GME_HB)
-
-#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82G35_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GME_HB)
-
-#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB)
-
-#define IS_PINEVIEW (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB)
-
-#define IS_SNB (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB)
-
-#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_EAGLELAKE_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \
- IS_SNB)
-
+int intel_gmch_probe(struct pci_dev *pdev,
+ struct agp_bridge_data *bridge);
+void intel_gmch_remove(struct pci_dev *pdev);
#endif
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 75e0a3497888..6b6760ea2435 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -15,6 +15,18 @@
* /fairy-tale-mode off
*/
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+#include <linux/agp_backend.h>
+#include <asm/smp.h>
+#include "agp.h"
+#include "intel-agp.h"
+#include <linux/intel-gtt.h>
+#include <drm/intel-gtt.h>
+
/*
* If we have Intel graphics, we're not going to have anything other than
* an Intel IOMMU. So make the correct use of the PCI DMA API contingent
@@ -23,11 +35,12 @@
*/
#ifdef CONFIG_DMAR
#define USE_PCI_DMA_API 1
+#else
+#define USE_PCI_DMA_API 0
#endif
/* Max amount of stolen space, anything above will be returned to Linux */
int intel_max_stolen = 32 * 1024 * 1024;
-EXPORT_SYMBOL(intel_max_stolen);
static const struct aper_size_info_fixed intel_i810_sizes[] =
{
@@ -55,32 +68,36 @@ static struct gatt_mask intel_i810_masks[] =
#define INTEL_AGP_CACHED_MEMORY_LLC_MLC 3
#define INTEL_AGP_CACHED_MEMORY_LLC_MLC_GFDT 4
-static struct gatt_mask intel_gen6_masks[] =
-{
- {.mask = I810_PTE_VALID | GEN6_PTE_UNCACHED,
- .type = INTEL_AGP_UNCACHED_MEMORY },
- {.mask = I810_PTE_VALID | GEN6_PTE_LLC,
- .type = INTEL_AGP_CACHED_MEMORY_LLC },
- {.mask = I810_PTE_VALID | GEN6_PTE_LLC | GEN6_PTE_GFDT,
- .type = INTEL_AGP_CACHED_MEMORY_LLC_GFDT },
- {.mask = I810_PTE_VALID | GEN6_PTE_LLC_MLC,
- .type = INTEL_AGP_CACHED_MEMORY_LLC_MLC },
- {.mask = I810_PTE_VALID | GEN6_PTE_LLC_MLC | GEN6_PTE_GFDT,
- .type = INTEL_AGP_CACHED_MEMORY_LLC_MLC_GFDT },
+struct intel_gtt_driver {
+ unsigned int gen : 8;
+ unsigned int is_g33 : 1;
+ unsigned int is_pineview : 1;
+ unsigned int is_ironlake : 1;
+ unsigned int dma_mask_size : 8;
+ /* Chipset specific GTT setup */
+ int (*setup)(void);
+ /* This should undo anything done in ->setup() save the unmapping
+ * of the mmio register file, that's done in the generic code. */
+ void (*cleanup)(void);
+ void (*write_entry)(dma_addr_t addr, unsigned int entry, unsigned int flags);
+ /* Flags is a more or less chipset specific opaque value.
+ * For chipsets that need to support old ums (non-gem) code, this
+ * needs to be identical to the various supported agp memory types! */
+ bool (*check_flags)(unsigned int flags);
+ void (*chipset_flush)(void);
};
static struct _intel_private {
+ struct intel_gtt base;
+ const struct intel_gtt_driver *driver;
struct pci_dev *pcidev; /* device one */
+ struct pci_dev *bridge_dev;
u8 __iomem *registers;
+ phys_addr_t gtt_bus_addr;
+ phys_addr_t gma_bus_addr;
+ phys_addr_t pte_bus_addr;
u32 __iomem *gtt; /* I915G */
int num_dcache_entries;
- /* gtt_entries is the number of gtt entries that are already mapped
- * to stolen memory. Stolen memory is larger than the memory mapped
- * through gtt_entries, as it includes some reserved space for the BIOS
- * popup and for the GTT.
- */
- int gtt_entries; /* i830+ */
- int gtt_total_size;
union {
void __iomem *i9xx_flush_page;
void *i8xx_flush_page;
@@ -88,23 +105,14 @@ static struct _intel_private {
struct page *i8xx_page;
struct resource ifp_resource;
int resource_valid;
+ struct page *scratch_page;
+ dma_addr_t scratch_page_dma;
} intel_private;
-#ifdef USE_PCI_DMA_API
-static int intel_agp_map_page(struct page *page, dma_addr_t *ret)
-{
- *ret = pci_map_page(intel_private.pcidev, page, 0,
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(intel_private.pcidev, *ret))
- return -EINVAL;
- return 0;
-}
-
-static void intel_agp_unmap_page(struct page *page, dma_addr_t dma)
-{
- pci_unmap_page(intel_private.pcidev, dma,
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
-}
+#define INTEL_GTT_GEN intel_private.driver->gen
+#define IS_G33 intel_private.driver->is_g33
+#define IS_PINEVIEW intel_private.driver->is_pineview
+#define IS_IRONLAKE intel_private.driver->is_ironlake
static void intel_agp_free_sglist(struct agp_memory *mem)
{
@@ -125,6 +133,9 @@ static int intel_agp_map_memory(struct agp_memory *mem)
struct scatterlist *sg;
int i;
+ if (mem->sg_list)
+ return 0; /* already mapped (for e.g. resume */
+
DBG("try mapping %lu pages\n", (unsigned long)mem->page_count);
if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL))
@@ -156,70 +167,17 @@ static void intel_agp_unmap_memory(struct agp_memory *mem)
intel_agp_free_sglist(mem);
}
-static void intel_agp_insert_sg_entries(struct agp_memory *mem,
- off_t pg_start, int mask_type)
-{
- struct scatterlist *sg;
- int i, j;
-
- j = pg_start;
-
- WARN_ON(!mem->num_sg);
-
- if (mem->num_sg == mem->page_count) {
- for_each_sg(mem->sg_list, sg, mem->page_count, i) {
- writel(agp_bridge->driver->mask_memory(agp_bridge,
- sg_dma_address(sg), mask_type),
- intel_private.gtt+j);
- j++;
- }
- } else {
- /* sg may merge pages, but we have to separate
- * per-page addr for GTT */
- unsigned int len, m;
-
- for_each_sg(mem->sg_list, sg, mem->num_sg, i) {
- len = sg_dma_len(sg) / PAGE_SIZE;
- for (m = 0; m < len; m++) {
- writel(agp_bridge->driver->mask_memory(agp_bridge,
- sg_dma_address(sg) + m * PAGE_SIZE,
- mask_type),
- intel_private.gtt+j);
- j++;
- }
- }
- }
- readl(intel_private.gtt+j-1);
-}
-
-#else
-
-static void intel_agp_insert_sg_entries(struct agp_memory *mem,
- off_t pg_start, int mask_type)
-{
- int i, j;
-
- for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
- writel(agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]), mask_type),
- intel_private.gtt+j);
- }
-
- readl(intel_private.gtt+j-1);
-}
-
-#endif
-
static int intel_i810_fetch_size(void)
{
u32 smram_miscc;
struct aper_size_info_fixed *values;
- pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc);
+ pci_read_config_dword(intel_private.bridge_dev,
+ I810_SMRAM_MISCC, &smram_miscc);
values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);
if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) {
- dev_warn(&agp_bridge->dev->dev, "i810 is disabled\n");
+ dev_warn(&intel_private.bridge_dev->dev, "i810 is disabled\n");
return 0;
}
if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) {
@@ -284,7 +242,7 @@ static void intel_i810_cleanup(void)
iounmap(intel_private.registers);
}
-static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode)
+static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode)
{
return;
}
@@ -319,34 +277,6 @@ static void i8xx_destroy_pages(struct page *page)
atomic_dec(&agp_bridge->current_memory_agp);
}
-static int intel_i830_type_to_mask_type(struct agp_bridge_data *bridge,
- int type)
-{
- if (type < AGP_USER_TYPES)
- return type;
- else if (type == AGP_USER_CACHED_MEMORY)
- return INTEL_AGP_CACHED_MEMORY;
- else
- return 0;
-}
-
-static int intel_gen6_type_to_mask_type(struct agp_bridge_data *bridge,
- int type)
-{
- unsigned int type_mask = type & ~AGP_USER_CACHED_MEMORY_GFDT;
- unsigned int gfdt = type & AGP_USER_CACHED_MEMORY_GFDT;
-
- if (type_mask == AGP_USER_UNCACHED_MEMORY)
- return INTEL_AGP_UNCACHED_MEMORY;
- else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC)
- return gfdt ? INTEL_AGP_CACHED_MEMORY_LLC_MLC_GFDT :
- INTEL_AGP_CACHED_MEMORY_LLC_MLC;
- else /* set 'normal'/'cached' to LLC by default */
- return gfdt ? INTEL_AGP_CACHED_MEMORY_LLC_GFDT :
- INTEL_AGP_CACHED_MEMORY_LLC;
-}
-
-
static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start,
int type)
{
@@ -514,8 +444,33 @@ static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge,
return addr | bridge->driver->masks[type].mask;
}
-static struct aper_size_info_fixed intel_i830_sizes[] =
+static int intel_gtt_setup_scratch_page(void)
{
+ struct page *page;
+ dma_addr_t dma_addr;
+
+ page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
+ if (page == NULL)
+ return -ENOMEM;
+ get_page(page);
+ set_pages_uc(page, 1);
+
+ if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) {
+ dma_addr = pci_map_page(intel_private.pcidev, page, 0,
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ if (pci_dma_mapping_error(intel_private.pcidev, dma_addr))
+ return -EINVAL;
+
+ intel_private.scratch_page_dma = dma_addr;
+ } else
+ intel_private.scratch_page_dma = page_to_phys(page);
+
+ intel_private.scratch_page = page;
+
+ return 0;
+}
+
+static const struct aper_size_info_fixed const intel_fake_agp_sizes[] = {
{128, 32768, 5},
/* The 64M mode still requires a 128k gatt */
{64, 16384, 5},
@@ -523,102 +478,49 @@ static struct aper_size_info_fixed intel_i830_sizes[] =
{512, 131072, 7},
};
-static void intel_i830_init_gtt_entries(void)
+static unsigned int intel_gtt_stolen_entries(void)
{
u16 gmch_ctrl;
- int gtt_entries = 0;
u8 rdct;
int local = 0;
static const int ddt[4] = { 0, 16, 32, 64 };
- int size; /* reserved space (in kb) at the top of stolen memory */
+ unsigned int overhead_entries, stolen_entries;
+ unsigned int stolen_size = 0;
- pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
+ pci_read_config_word(intel_private.bridge_dev,
+ I830_GMCH_CTRL, &gmch_ctrl);
- if (IS_I965) {
- u32 pgetbl_ctl;
- pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL);
+ if (INTEL_GTT_GEN > 4 || IS_PINEVIEW)
+ overhead_entries = 0;
+ else
+ overhead_entries = intel_private.base.gtt_mappable_entries
+ / 1024;
- /* The 965 has a field telling us the size of the GTT,
- * which may be larger than what is necessary to map the
- * aperture.
- */
- switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) {
- case I965_PGETBL_SIZE_128KB:
- size = 128;
- break;
- case I965_PGETBL_SIZE_256KB:
- size = 256;
- break;
- case I965_PGETBL_SIZE_512KB:
- size = 512;
- break;
- case I965_PGETBL_SIZE_1MB:
- size = 1024;
- break;
- case I965_PGETBL_SIZE_2MB:
- size = 2048;
- break;
- case I965_PGETBL_SIZE_1_5MB:
- size = 1024 + 512;
- break;
- default:
- dev_info(&intel_private.pcidev->dev,
- "unknown page table size, assuming 512KB\n");
- size = 512;
- }
- size += 4; /* add in BIOS popup space */
- } else if (IS_G33 && !IS_PINEVIEW) {
- /* G33's GTT size defined in gmch_ctrl */
- switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) {
- case G33_PGETBL_SIZE_1M:
- size = 1024;
- break;
- case G33_PGETBL_SIZE_2M:
- size = 2048;
- break;
- default:
- dev_info(&agp_bridge->dev->dev,
- "unknown page table size 0x%x, assuming 512KB\n",
- (gmch_ctrl & G33_PGETBL_SIZE_MASK));
- size = 512;
- }
- size += 4;
- } else if (IS_G4X || IS_PINEVIEW) {
- /* On 4 series hardware, GTT stolen is separate from graphics
- * stolen, ignore it in stolen gtt entries counting. However,
- * 4KB of the stolen memory doesn't get mapped to the GTT.
- */
- size = 4;
- } else {
- /* On previous hardware, the GTT size was just what was
- * required to map the aperture.
- */
- size = agp_bridge->driver->fetch_size() + 4;
- }
+ overhead_entries += 1; /* BIOS popup */
- if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB ||
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) {
+ if (intel_private.bridge_dev->device == PCI_DEVICE_ID_INTEL_82830_HB ||
+ intel_private.bridge_dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) {
switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
case I830_GMCH_GMS_STOLEN_512:
- gtt_entries = KB(512) - KB(size);
+ stolen_size = KB(512);
break;
case I830_GMCH_GMS_STOLEN_1024:
- gtt_entries = MB(1) - KB(size);
+ stolen_size = MB(1);
break;
case I830_GMCH_GMS_STOLEN_8192:
- gtt_entries = MB(8) - KB(size);
+ stolen_size = MB(8);
break;
case I830_GMCH_GMS_LOCAL:
rdct = readb(intel_private.registers+I830_RDRAM_CHANNEL_TYPE);
- gtt_entries = (I830_RDRAM_ND(rdct) + 1) *
+ stolen_size = (I830_RDRAM_ND(rdct) + 1) *
MB(ddt[I830_RDRAM_DDT(rdct)]);
local = 1;
break;
default:
- gtt_entries = 0;
+ stolen_size = 0;
break;
}
- } else if (IS_SNB) {
+ } else if (INTEL_GTT_GEN == 6) {
/*
* SandyBridge has new memory control reg at 0x50.w
*/
@@ -626,149 +528,292 @@ static void intel_i830_init_gtt_entries(void)
pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl);
switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) {
case SNB_GMCH_GMS_STOLEN_32M:
- gtt_entries = MB(32) - KB(size);
+ stolen_size = MB(32);
break;
case SNB_GMCH_GMS_STOLEN_64M:
- gtt_entries = MB(64) - KB(size);
+ stolen_size = MB(64);
break;
case SNB_GMCH_GMS_STOLEN_96M:
- gtt_entries = MB(96) - KB(size);
+ stolen_size = MB(96);
break;
case SNB_GMCH_GMS_STOLEN_128M:
- gtt_entries = MB(128) - KB(size);
+ stolen_size = MB(128);
break;
case SNB_GMCH_GMS_STOLEN_160M:
- gtt_entries = MB(160) - KB(size);
+ stolen_size = MB(160);
break;
case SNB_GMCH_GMS_STOLEN_192M:
- gtt_entries = MB(192) - KB(size);
+ stolen_size = MB(192);
break;
case SNB_GMCH_GMS_STOLEN_224M:
- gtt_entries = MB(224) - KB(size);
+ stolen_size = MB(224);
break;
case SNB_GMCH_GMS_STOLEN_256M:
- gtt_entries = MB(256) - KB(size);
+ stolen_size = MB(256);
break;
case SNB_GMCH_GMS_STOLEN_288M:
- gtt_entries = MB(288) - KB(size);
+ stolen_size = MB(288);
break;
case SNB_GMCH_GMS_STOLEN_320M:
- gtt_entries = MB(320) - KB(size);
+ stolen_size = MB(320);
break;
case SNB_GMCH_GMS_STOLEN_352M:
- gtt_entries = MB(352) - KB(size);
+ stolen_size = MB(352);
break;
case SNB_GMCH_GMS_STOLEN_384M:
- gtt_entries = MB(384) - KB(size);
+ stolen_size = MB(384);
break;
case SNB_GMCH_GMS_STOLEN_416M:
- gtt_entries = MB(416) - KB(size);
+ stolen_size = MB(416);
break;
case SNB_GMCH_GMS_STOLEN_448M:
- gtt_entries = MB(448) - KB(size);
+ stolen_size = MB(448);
break;
case SNB_GMCH_GMS_STOLEN_480M:
- gtt_entries = MB(480) - KB(size);
+ stolen_size = MB(480);
break;
case SNB_GMCH_GMS_STOLEN_512M:
- gtt_entries = MB(512) - KB(size);
+ stolen_size = MB(512);
break;
}
} else {
switch (gmch_ctrl & I855_GMCH_GMS_MASK) {
case I855_GMCH_GMS_STOLEN_1M:
- gtt_entries = MB(1) - KB(size);
+ stolen_size = MB(1);
break;
case I855_GMCH_GMS_STOLEN_4M:
- gtt_entries = MB(4) - KB(size);
+ stolen_size = MB(4);
break;
case I855_GMCH_GMS_STOLEN_8M:
- gtt_entries = MB(8) - KB(size);
+ stolen_size = MB(8);
break;
case I855_GMCH_GMS_STOLEN_16M:
- gtt_entries = MB(16) - KB(size);
+ stolen_size = MB(16);
break;
case I855_GMCH_GMS_STOLEN_32M:
- gtt_entries = MB(32) - KB(size);
+ stolen_size = MB(32);
break;
case I915_GMCH_GMS_STOLEN_48M:
- /* Check it's really I915G */
- if (IS_I915 || IS_I965 || IS_G33 || IS_G4X)
- gtt_entries = MB(48) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(48);
break;
case I915_GMCH_GMS_STOLEN_64M:
- /* Check it's really I915G */
- if (IS_I915 || IS_I965 || IS_G33 || IS_G4X)
- gtt_entries = MB(64) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(64);
break;
case G33_GMCH_GMS_STOLEN_128M:
- if (IS_G33 || IS_I965 || IS_G4X)
- gtt_entries = MB(128) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(128);
break;
case G33_GMCH_GMS_STOLEN_256M:
- if (IS_G33 || IS_I965 || IS_G4X)
- gtt_entries = MB(256) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(256);
break;
case INTEL_GMCH_GMS_STOLEN_96M:
- if (IS_I965 || IS_G4X)
- gtt_entries = MB(96) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(96);
break;
case INTEL_GMCH_GMS_STOLEN_160M:
- if (IS_I965 || IS_G4X)
- gtt_entries = MB(160) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(160);
break;
case INTEL_GMCH_GMS_STOLEN_224M:
- if (IS_I965 || IS_G4X)
- gtt_entries = MB(224) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(224);
break;
case INTEL_GMCH_GMS_STOLEN_352M:
- if (IS_I965 || IS_G4X)
- gtt_entries = MB(352) - KB(size);
- else
- gtt_entries = 0;
+ stolen_size = MB(352);
break;
default:
- gtt_entries = 0;
+ stolen_size = 0;
break;
}
}
- if (!local && gtt_entries > intel_max_stolen) {
- dev_info(&agp_bridge->dev->dev,
+
+ if (!local && stolen_size > intel_max_stolen) {
+ dev_info(&intel_private.bridge_dev->dev,
"detected %dK stolen memory, trimming to %dK\n",
- gtt_entries / KB(1), intel_max_stolen / KB(1));
- gtt_entries = intel_max_stolen / KB(4);
- } else if (gtt_entries > 0) {
- dev_info(&agp_bridge->dev->dev, "detected %dK %s memory\n",
- gtt_entries / KB(1), local ? "local" : "stolen");
- gtt_entries /= KB(4);
+ stolen_size / KB(1), intel_max_stolen / KB(1));
+ stolen_size = intel_max_stolen;
+ } else if (stolen_size > 0) {
+ dev_info(&intel_private.bridge_dev->dev, "detected %dK %s memory\n",
+ stolen_size / KB(1), local ? "local" : "stolen");
} else {
- dev_info(&agp_bridge->dev->dev,
+ dev_info(&intel_private.bridge_dev->dev,
"no pre-allocated video memory detected\n");
- gtt_entries = 0;
+ stolen_size = 0;
+ }
+
+ stolen_entries = stolen_size/KB(4) - overhead_entries;
+
+ return stolen_entries;
+}
+
+static unsigned int intel_gtt_total_entries(void)
+{
+ int size;
+
+ if (IS_G33 || INTEL_GTT_GEN == 4 || INTEL_GTT_GEN == 5) {
+ u32 pgetbl_ctl;
+ pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL);
+
+ switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) {
+ case I965_PGETBL_SIZE_128KB:
+ size = KB(128);
+ break;
+ case I965_PGETBL_SIZE_256KB:
+ size = KB(256);
+ break;
+ case I965_PGETBL_SIZE_512KB:
+ size = KB(512);
+ break;
+ case I965_PGETBL_SIZE_1MB:
+ size = KB(1024);
+ break;
+ case I965_PGETBL_SIZE_2MB:
+ size = KB(2048);
+ break;
+ case I965_PGETBL_SIZE_1_5MB:
+ size = KB(1024 + 512);
+ break;
+ default:
+ dev_info(&intel_private.pcidev->dev,
+ "unknown page table size, assuming 512KB\n");
+ size = KB(512);
+ }
+
+ return size/4;
+ } else if (INTEL_GTT_GEN == 6) {
+ u16 snb_gmch_ctl;
+
+ pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl);
+ switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) {
+ default:
+ case SNB_GTT_SIZE_0M:
+ printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl);
+ size = MB(0);
+ break;
+ case SNB_GTT_SIZE_1M:
+ size = MB(1);
+ break;
+ case SNB_GTT_SIZE_2M:
+ size = MB(2);
+ break;
+ }
+ return size/4;
+ } else {
+ /* On previous hardware, the GTT size was just what was
+ * required to map the aperture.
+ */
+ return intel_private.base.gtt_mappable_entries;
+ }
+}
+
+static unsigned int intel_gtt_mappable_entries(void)
+{
+ unsigned int aperture_size;
+
+ if (INTEL_GTT_GEN == 2) {
+ u16 gmch_ctrl;
+
+ pci_read_config_word(intel_private.bridge_dev,
+ I830_GMCH_CTRL, &gmch_ctrl);
+
+ if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_64M)
+ aperture_size = MB(64);
+ else
+ aperture_size = MB(128);
+ } else {
+ /* 9xx supports large sizes, just look at the length */
+ aperture_size = pci_resource_len(intel_private.pcidev, 2);
}
- intel_private.gtt_entries = gtt_entries;
+ return aperture_size >> PAGE_SHIFT;
}
-static void intel_i830_fini_flush(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,
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ put_page(intel_private.scratch_page);
+ __free_page(intel_private.scratch_page);
+}
+
+static void intel_gtt_cleanup(void)
+{
+ intel_private.driver->cleanup();
+
+ iounmap(intel_private.gtt);
+ iounmap(intel_private.registers);
+
+ intel_gtt_teardown_scratch_page();
+}
+
+static int intel_gtt_init(void)
+{
+ u32 gtt_map_size;
+ int ret;
+
+ ret = intel_private.driver->setup();
+ if (ret != 0)
+ return ret;
+
+ intel_private.base.gtt_mappable_entries = intel_gtt_mappable_entries();
+ intel_private.base.gtt_total_entries = intel_gtt_total_entries();
+
+ dev_info(&intel_private.bridge_dev->dev,
+ "detected gtt size: %dK total, %dK mappable\n",
+ intel_private.base.gtt_total_entries * 4,
+ intel_private.base.gtt_mappable_entries * 4);
+
+ gtt_map_size = intel_private.base.gtt_total_entries * 4;
+
+ intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
+ gtt_map_size);
+ if (!intel_private.gtt) {
+ intel_private.driver->cleanup();
+ iounmap(intel_private.registers);
+ return -ENOMEM;
+ }
+
+ global_cache_flush(); /* FIXME: ? */
+
+ /* we have to call this as early as possible after the MMIO base address is known */
+ intel_private.base.gtt_stolen_entries = intel_gtt_stolen_entries();
+ if (intel_private.base.gtt_stolen_entries == 0) {
+ intel_private.driver->cleanup();
+ iounmap(intel_private.registers);
+ iounmap(intel_private.gtt);
+ return -ENOMEM;
+ }
+
+ ret = intel_gtt_setup_scratch_page();
+ if (ret != 0) {
+ intel_gtt_cleanup();
+ return ret;
+ }
+
+ return 0;
+}
+
+static int intel_fake_agp_fetch_size(void)
+{
+ int num_sizes = ARRAY_SIZE(intel_fake_agp_sizes);
+ unsigned int aper_size;
+ int i;
+
+ aper_size = (intel_private.base.gtt_mappable_entries << PAGE_SHIFT)
+ / MB(1);
+
+ for (i = 0; i < num_sizes; i++) {
+ if (aper_size == intel_fake_agp_sizes[i].size) {
+ agp_bridge->current_size =
+ (void *) (intel_fake_agp_sizes + i);
+ return aper_size;
+ }
+ }
+
+ return 0;
+}
+
+static void i830_cleanup(void)
{
kunmap(intel_private.i8xx_page);
intel_private.i8xx_flush_page = NULL;
- unmap_page_from_agp(intel_private.i8xx_page);
__free_page(intel_private.i8xx_page);
intel_private.i8xx_page = NULL;
@@ -780,13 +825,13 @@ static void intel_i830_setup_flush(void)
if (intel_private.i8xx_page)
return;
- intel_private.i8xx_page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32);
+ intel_private.i8xx_page = alloc_page(GFP_KERNEL);
if (!intel_private.i8xx_page)
return;
intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page);
if (!intel_private.i8xx_flush_page)
- intel_i830_fini_flush();
+ i830_cleanup();
}
/* The chipset_flush interface needs to get data that has already been
@@ -799,7 +844,7 @@ static void intel_i830_setup_flush(void)
* that buffer out, we just fill 1KB and clflush it out, on the assumption
* that it'll push whatever was in there out. It appears to work.
*/
-static void intel_i830_chipset_flush(struct agp_bridge_data *bridge)
+static void i830_chipset_flush(void)
{
unsigned int *pg = intel_private.i8xx_flush_page;
@@ -811,169 +856,184 @@ static void intel_i830_chipset_flush(struct agp_bridge_data *bridge)
printk(KERN_ERR "Timed out waiting for cache flush.\n");
}
-/* The intel i830 automatically initializes the agp aperture during POST.
- * Use the memory already set aside for in the GTT.
- */
-static int intel_i830_create_gatt_table(struct agp_bridge_data *bridge)
+static void i830_write_entry(dma_addr_t addr, unsigned int entry,
+ unsigned int flags)
{
- int page_order;
- struct aper_size_info_fixed *size;
- int num_entries;
- u32 temp;
+ u32 pte_flags = I810_PTE_VALID;
+
+ switch (flags) {
+ case AGP_DCACHE_MEMORY:
+ pte_flags |= I810_PTE_LOCAL;
+ break;
+ case AGP_USER_CACHED_MEMORY:
+ pte_flags |= I830_PTE_SYSTEM_CACHED;
+ break;
+ }
- size = agp_bridge->current_size;
- page_order = size->page_order;
- num_entries = size->num_entries;
- agp_bridge->gatt_table_real = NULL;
+ writel(addr | pte_flags, intel_private.gtt + entry);
+}
- pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp);
- temp &= 0xfff80000;
+static void intel_enable_gtt(void)
+{
+ u32 gma_addr;
+ u16 gmch_ctrl;
- intel_private.registers = ioremap(temp, 128 * 4096);
- if (!intel_private.registers)
- return -ENOMEM;
+ if (INTEL_GTT_GEN == 2)
+ pci_read_config_dword(intel_private.pcidev, I810_GMADDR,
+ &gma_addr);
+ else
+ pci_read_config_dword(intel_private.pcidev, I915_GMADDR,
+ &gma_addr);
- temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000;
- global_cache_flush(); /* FIXME: ?? */
+ intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK);
- /* we have to call this as early as possible after the MMIO base address is known */
- intel_i830_init_gtt_entries();
- if (intel_private.gtt_entries == 0) {
- iounmap(intel_private.registers);
+ pci_read_config_word(intel_private.bridge_dev, I830_GMCH_CTRL, &gmch_ctrl);
+ gmch_ctrl |= I830_GMCH_ENABLED;
+ pci_write_config_word(intel_private.bridge_dev, I830_GMCH_CTRL, gmch_ctrl);
+
+ writel(intel_private.pte_bus_addr|I810_PGETBL_ENABLED,
+ intel_private.registers+I810_PGETBL_CTL);
+ readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */
+}
+
+static int i830_setup(void)
+{
+ u32 reg_addr;
+
+ pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &reg_addr);
+ reg_addr &= 0xfff80000;
+
+ intel_private.registers = ioremap(reg_addr, KB(64));
+ if (!intel_private.registers)
return -ENOMEM;
- }
- agp_bridge->gatt_table = NULL;
+ intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE;
+ intel_private.pte_bus_addr =
+ readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000;
- agp_bridge->gatt_bus_addr = temp;
+ intel_i830_setup_flush();
return 0;
}
-/* Return the gatt table to a sane state. Use the top of stolen
- * memory for the GTT.
- */
-static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge)
+static int intel_fake_agp_create_gatt_table(struct agp_bridge_data *bridge)
{
+ agp_bridge->gatt_table_real = NULL;
+ agp_bridge->gatt_table = NULL;
+ agp_bridge->gatt_bus_addr = 0;
+
return 0;
}
-static int intel_i830_fetch_size(void)
+static int intel_fake_agp_free_gatt_table(struct agp_bridge_data *bridge)
{
- u16 gmch_ctrl;
- struct aper_size_info_fixed *values;
-
- values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);
-
- if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB &&
- agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) {
- /* 855GM/852GM/865G has 128MB aperture size */
- agp_bridge->current_size = (void *) values;
- agp_bridge->aperture_size_idx = 0;
- return values[0].size;
- }
-
- pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
-
- if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) {
- agp_bridge->current_size = (void *) values;
- agp_bridge->aperture_size_idx = 0;
- return values[0].size;
- } else {
- agp_bridge->current_size = (void *) (values + 1);
- agp_bridge->aperture_size_idx = 1;
- return values[1].size;
- }
-
return 0;
}
-static int intel_i830_configure(void)
+static int intel_fake_agp_configure(void)
{
- struct aper_size_info_fixed *current_size;
- u32 temp;
- u16 gmch_ctrl;
int i;
- current_size = A_SIZE_FIX(agp_bridge->current_size);
-
- pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
-
- pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
- gmch_ctrl |= I830_GMCH_ENABLED;
- pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl);
+ intel_enable_gtt();
- writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL);
- readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */
+ agp_bridge->gart_bus_addr = intel_private.gma_bus_addr;
- if (agp_bridge->driver->needs_scratch_page) {
- for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) {
- writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4));
- }
- readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI Posting. */
+ for (i = intel_private.base.gtt_stolen_entries;
+ i < intel_private.base.gtt_total_entries; i++) {
+ intel_private.driver->write_entry(intel_private.scratch_page_dma,
+ i, 0);
}
+ readl(intel_private.gtt+i-1); /* PCI Posting. */
global_cache_flush();
- intel_i830_setup_flush();
return 0;
}
-static void intel_i830_cleanup(void)
+static bool i830_check_flags(unsigned int flags)
{
- iounmap(intel_private.registers);
+ switch (flags) {
+ case 0:
+ case AGP_PHYS_MEMORY:
+ case AGP_USER_CACHED_MEMORY:
+ case AGP_USER_MEMORY:
+ return true;
+ }
+
+ return false;
}
-static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start,
- int type)
+static void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
+ unsigned int sg_len,
+ unsigned int pg_start,
+ unsigned int flags)
{
- int i, j, num_entries;
- void *temp;
+ struct scatterlist *sg;
+ unsigned int len, m;
+ int i, j;
+
+ j = pg_start;
+
+ /* sg may merge pages, but we have to separate
+ * per-page addr for GTT */
+ for_each_sg(sg_list, sg, sg_len, i) {
+ len = sg_dma_len(sg) >> PAGE_SHIFT;
+ for (m = 0; m < len; m++) {
+ dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
+ intel_private.driver->write_entry(addr,
+ j, flags);
+ j++;
+ }
+ }
+ readl(intel_private.gtt+j-1);
+}
+
+static int intel_fake_agp_insert_entries(struct agp_memory *mem,
+ off_t pg_start, int type)
+{
+ int i, j;
int ret = -EINVAL;
- int mask_type;
if (mem->page_count == 0)
goto out;
- temp = agp_bridge->current_size;
- num_entries = A_SIZE_FIX(temp)->num_entries;
-
- if (pg_start < intel_private.gtt_entries) {
+ if (pg_start < intel_private.base.gtt_stolen_entries) {
dev_printk(KERN_DEBUG, &intel_private.pcidev->dev,
- "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n",
- pg_start, intel_private.gtt_entries);
+ "pg_start == 0x%.8lx, gtt_stolen_entries == 0x%.8x\n",
+ pg_start, intel_private.base.gtt_stolen_entries);
dev_info(&intel_private.pcidev->dev,
"trying to insert into local/stolen memory\n");
goto out_err;
}
- if ((pg_start + mem->page_count) > num_entries)
+ if ((pg_start + mem->page_count) > intel_private.base.gtt_total_entries)
goto out_err;
- /* The i830 can't check the GTT for entries since its read only,
- * depend on the caller to make the correct offset decisions.
- */
-
if (type != mem->type)
goto out_err;
- mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
-
- if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY &&
- mask_type != INTEL_AGP_CACHED_MEMORY)
+ if (!intel_private.driver->check_flags(type))
goto out_err;
if (!mem->is_flushed)
global_cache_flush();
- for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
- writel(agp_bridge->driver->mask_memory(agp_bridge,
- page_to_phys(mem->pages[i]), mask_type),
- intel_private.registers+I810_PTE_BASE+(j*4));
+ if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) {
+ ret = intel_agp_map_memory(mem);
+ if (ret != 0)
+ return ret;
+
+ intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg,
+ pg_start, type);
+ } else {
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ dma_addr_t addr = page_to_phys(mem->pages[i]);
+ intel_private.driver->write_entry(addr,
+ j, type);
+ }
+ readl(intel_private.gtt+j-1);
}
- readl(intel_private.registers+I810_PTE_BASE+((j-1)*4));
out:
ret = 0;
@@ -982,29 +1042,39 @@ out_err:
return ret;
}
-static int intel_i830_remove_entries(struct agp_memory *mem, off_t pg_start,
- int type)
+static int intel_fake_agp_remove_entries(struct agp_memory *mem,
+ off_t pg_start, int type)
{
int i;
if (mem->page_count == 0)
return 0;
- if (pg_start < intel_private.gtt_entries) {
+ if (pg_start < intel_private.base.gtt_stolen_entries) {
dev_info(&intel_private.pcidev->dev,
"trying to disable local/stolen memory\n");
return -EINVAL;
}
+ if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2)
+ intel_agp_unmap_memory(mem);
+
for (i = pg_start; i < (mem->page_count + pg_start); i++) {
- writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4));
+ intel_private.driver->write_entry(intel_private.scratch_page_dma,
+ i, 0);
}
- readl(intel_private.registers+I810_PTE_BASE+((i-1)*4));
+ readl(intel_private.gtt+i-1);
return 0;
}
-static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type)
+static void intel_fake_agp_chipset_flush(struct agp_bridge_data *bridge)
+{
+ intel_private.driver->chipset_flush();
+}
+
+static struct agp_memory *intel_fake_agp_alloc_by_type(size_t pg_count,
+ int type)
{
if (type == AGP_PHYS_MEMORY)
return alloc_agpphysmem_i8xx(pg_count, type);
@@ -1015,9 +1085,9 @@ static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type)
static int intel_alloc_chipset_flush_resource(void)
{
int ret;
- ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE,
+ ret = pci_bus_alloc_resource(intel_private.bridge_dev->bus, &intel_private.ifp_resource, PAGE_SIZE,
PAGE_SIZE, PCIBIOS_MIN_MEM, 0,
- pcibios_align_resource, agp_bridge->dev);
+ pcibios_align_resource, intel_private.bridge_dev);
return ret;
}
@@ -1027,11 +1097,11 @@ static void intel_i915_setup_chipset_flush(void)
int ret;
u32 temp;
- pci_read_config_dword(agp_bridge->dev, I915_IFPADDR, &temp);
+ pci_read_config_dword(intel_private.bridge_dev, I915_IFPADDR, &temp);
if (!(temp & 0x1)) {
intel_alloc_chipset_flush_resource();
intel_private.resource_valid = 1;
- pci_write_config_dword(agp_bridge->dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1);
+ pci_write_config_dword(intel_private.bridge_dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1);
} else {
temp &= ~1;
@@ -1050,17 +1120,17 @@ static void intel_i965_g33_setup_chipset_flush(void)
u32 temp_hi, temp_lo;
int ret;
- pci_read_config_dword(agp_bridge->dev, I965_IFPADDR + 4, &temp_hi);
- pci_read_config_dword(agp_bridge->dev, I965_IFPADDR, &temp_lo);
+ pci_read_config_dword(intel_private.bridge_dev, I965_IFPADDR + 4, &temp_hi);
+ pci_read_config_dword(intel_private.bridge_dev, I965_IFPADDR, &temp_lo);
if (!(temp_lo & 0x1)) {
intel_alloc_chipset_flush_resource();
intel_private.resource_valid = 1;
- pci_write_config_dword(agp_bridge->dev, I965_IFPADDR + 4,
+ pci_write_config_dword(intel_private.bridge_dev, I965_IFPADDR + 4,
upper_32_bits(intel_private.ifp_resource.start));
- pci_write_config_dword(agp_bridge->dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1);
+ pci_write_config_dword(intel_private.bridge_dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1);
} else {
u64 l64;
@@ -1083,7 +1153,7 @@ static void intel_i9xx_setup_flush(void)
if (intel_private.ifp_resource.start)
return;
- if (IS_SNB)
+ if (INTEL_GTT_GEN == 6)
return;
/* setup a resource for this object */
@@ -1091,7 +1161,7 @@ static void intel_i9xx_setup_flush(void)
intel_private.ifp_resource.flags = IORESOURCE_MEM;
/* Setup chipset flush for 915 */
- if (IS_I965 || IS_G33 || IS_G4X) {
+ if (IS_G33 || INTEL_GTT_GEN >= 4) {
intel_i965_g33_setup_chipset_flush();
} else {
intel_i915_setup_chipset_flush();
@@ -1104,41 +1174,7 @@ static void intel_i9xx_setup_flush(void)
"can't ioremap flush page - no chipset flushing\n");
}
-static int intel_i9xx_configure(void)
-{
- struct aper_size_info_fixed *current_size;
- u32 temp;
- u16 gmch_ctrl;
- int i;
-
- current_size = A_SIZE_FIX(agp_bridge->current_size);
-
- pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &temp);
-
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
-
- pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
- gmch_ctrl |= I830_GMCH_ENABLED;
- pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl);
-
- writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL);
- readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */
-
- if (agp_bridge->driver->needs_scratch_page) {
- for (i = intel_private.gtt_entries; i < intel_private.gtt_total_size; i++) {
- writel(agp_bridge->scratch_page, intel_private.gtt+i);
- }
- readl(intel_private.gtt+i-1); /* PCI Posting. */
- }
-
- global_cache_flush();
-
- intel_i9xx_setup_flush();
-
- return 0;
-}
-
-static void intel_i915_cleanup(void)
+static void i9xx_cleanup(void)
{
if (intel_private.i9xx_flush_page)
iounmap(intel_private.i9xx_flush_page);
@@ -1146,320 +1182,93 @@ static void intel_i915_cleanup(void)
release_resource(&intel_private.ifp_resource);
intel_private.ifp_resource.start = 0;
intel_private.resource_valid = 0;
- iounmap(intel_private.gtt);
- iounmap(intel_private.registers);
}
-static void intel_i915_chipset_flush(struct agp_bridge_data *bridge)
+static void i9xx_chipset_flush(void)
{
if (intel_private.i9xx_flush_page)
writel(1, intel_private.i9xx_flush_page);
}
-static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start,
- int type)
+static void i965_write_entry(dma_addr_t addr, unsigned int entry,
+ unsigned int flags)
{
- int num_entries;
- void *temp;
- int ret = -EINVAL;
- int mask_type;
-
- if (mem->page_count == 0)
- goto out;
-
- temp = agp_bridge->current_size;
- num_entries = A_SIZE_FIX(temp)->num_entries;
-
- if (pg_start < intel_private.gtt_entries) {
- dev_printk(KERN_DEBUG, &intel_private.pcidev->dev,
- "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n",
- pg_start, intel_private.gtt_entries);
-
- dev_info(&intel_private.pcidev->dev,
- "trying to insert into local/stolen memory\n");
- goto out_err;
- }
-
- if ((pg_start + mem->page_count) > num_entries)
- goto out_err;
-
- /* The i915 can't check the GTT for entries since it's read only;
- * depend on the caller to make the correct offset decisions.
- */
-
- if (type != mem->type)
- goto out_err;
-
- mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
-
- if (!IS_SNB && mask_type != 0 && mask_type != AGP_PHYS_MEMORY &&
- mask_type != INTEL_AGP_CACHED_MEMORY)
- goto out_err;
-
- if (!mem->is_flushed)
- global_cache_flush();
-
- intel_agp_insert_sg_entries(mem, pg_start, mask_type);
-
- out:
- ret = 0;
- out_err:
- mem->is_flushed = true;
- return ret;
+ /* Shift high bits down */
+ addr |= (addr >> 28) & 0xf0;
+ writel(addr | I810_PTE_VALID, intel_private.gtt + entry);
}
-static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start,
- int type)
+static bool gen6_check_flags(unsigned int flags)
{
- int i;
-
- if (mem->page_count == 0)
- return 0;
-
- if (pg_start < intel_private.gtt_entries) {
- dev_info(&intel_private.pcidev->dev,
- "trying to disable local/stolen memory\n");
- return -EINVAL;
- }
-
- for (i = pg_start; i < (mem->page_count + pg_start); i++)
- writel(agp_bridge->scratch_page, intel_private.gtt+i);
-
- readl(intel_private.gtt+i-1);
-
- return 0;
+ return true;
}
-/* Return the aperture size by just checking the resource length. The effect
- * described in the spec of the MSAC registers is just changing of the
- * resource size.
- */
-static int intel_i9xx_fetch_size(void)
+static void gen6_write_entry(dma_addr_t addr, unsigned int entry,
+ unsigned int flags)
{
- int num_sizes = ARRAY_SIZE(intel_i830_sizes);
- int aper_size; /* size in megabytes */
- int i;
-
- aper_size = pci_resource_len(intel_private.pcidev, 2) / MB(1);
+ unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT;
+ unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT;
+ u32 pte_flags;
- for (i = 0; i < num_sizes; i++) {
- if (aper_size == intel_i830_sizes[i].size) {
- agp_bridge->current_size = intel_i830_sizes + i;
- return aper_size;
- }
+ if (type_mask == AGP_USER_UNCACHED_MEMORY)
+ pte_flags = GEN6_PTE_UNCACHED | I810_PTE_VALID;
+ else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) {
+ pte_flags = GEN6_PTE_LLC | I810_PTE_VALID;
+ if (gfdt)
+ pte_flags |= GEN6_PTE_GFDT;
+ } else { /* set 'normal'/'cached' to LLC by default */
+ pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID;
+ if (gfdt)
+ pte_flags |= GEN6_PTE_GFDT;
}
- return 0;
+ /* gen6 has bit11-4 for physical addr bit39-32 */
+ addr |= (addr >> 28) & 0xff0;
+ writel(addr | pte_flags, intel_private.gtt + entry);
}
-static int intel_i915_get_gtt_size(void)
+static void gen6_cleanup(void)
{
- int size;
-
- if (IS_G33) {
- u16 gmch_ctrl;
-
- /* G33's GTT size defined in gmch_ctrl */
- pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl);
- switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
- case I830_GMCH_GMS_STOLEN_512:
- size = 512;
- break;
- case I830_GMCH_GMS_STOLEN_1024:
- size = 1024;
- break;
- case I830_GMCH_GMS_STOLEN_8192:
- size = 8*1024;
- break;
- default:
- dev_info(&agp_bridge->dev->dev,
- "unknown page table size 0x%x, assuming 512KB\n",
- (gmch_ctrl & I830_GMCH_GMS_MASK));
- size = 512;
- }
- } else {
- /* On previous hardware, the GTT size was just what was
- * required to map the aperture.
- */
- size = agp_bridge->driver->fetch_size();
- }
-
- return KB(size);
}
-/* The intel i915 automatically initializes the agp aperture during POST.
- * Use the memory already set aside for in the GTT.
- */
-static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge)
+static int i9xx_setup(void)
{
- int page_order;
- struct aper_size_info_fixed *size;
- int num_entries;
- u32 temp, temp2;
- int gtt_map_size;
-
- size = agp_bridge->current_size;
- page_order = size->page_order;
- num_entries = size->num_entries;
- agp_bridge->gatt_table_real = NULL;
-
- pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp);
- pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, &temp2);
+ u32 reg_addr;
- gtt_map_size = intel_i915_get_gtt_size();
-
- intel_private.gtt = ioremap(temp2, gtt_map_size);
- if (!intel_private.gtt)
- return -ENOMEM;
-
- intel_private.gtt_total_size = gtt_map_size / 4;
-
- temp &= 0xfff80000;
-
- intel_private.registers = ioremap(temp, 128 * 4096);
- if (!intel_private.registers) {
- iounmap(intel_private.gtt);
- return -ENOMEM;
- }
+ pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &reg_addr);
- temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000;
- global_cache_flush(); /* FIXME: ? */
+ reg_addr &= 0xfff80000;
- /* we have to call this as early as possible after the MMIO base address is known */
- intel_i830_init_gtt_entries();
- if (intel_private.gtt_entries == 0) {
- iounmap(intel_private.gtt);
- iounmap(intel_private.registers);
+ intel_private.registers = ioremap(reg_addr, 128 * 4096);
+ if (!intel_private.registers)
return -ENOMEM;
- }
-
- agp_bridge->gatt_table = NULL;
-
- agp_bridge->gatt_bus_addr = temp;
-
- return 0;
-}
-
-/*
- * The i965 supports 36-bit physical addresses, but to keep
- * the format of the GTT the same, the bits that don't fit
- * in a 32-bit word are shifted down to bits 4..7.
- *
- * Gcc is smart enough to notice that "(addr >> 28) & 0xf0"
- * is always zero on 32-bit architectures, so no need to make
- * this conditional.
- */
-static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge,
- dma_addr_t addr, int type)
-{
- /* Shift high bits down */
- addr |= (addr >> 28) & 0xf0;
-
- /* Type checking must be done elsewhere */
- return addr | bridge->driver->masks[type].mask;
-}
-static unsigned long intel_gen6_mask_memory(struct agp_bridge_data *bridge,
- dma_addr_t addr, int type)
-{
- /* gen6 has bit11-4 for physical addr bit39-32 */
- addr |= (addr >> 28) & 0xff0;
+ if (INTEL_GTT_GEN == 3) {
+ u32 gtt_addr;
- /* Type checking must be done elsewhere */
- return addr | bridge->driver->masks[type].mask;
-}
-
-static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
-{
- u16 snb_gmch_ctl;
-
- switch (agp_bridge->dev->device) {
- case PCI_DEVICE_ID_INTEL_GM45_HB:
- case PCI_DEVICE_ID_INTEL_EAGLELAKE_HB:
- case PCI_DEVICE_ID_INTEL_Q45_HB:
- case PCI_DEVICE_ID_INTEL_G45_HB:
- case PCI_DEVICE_ID_INTEL_G41_HB:
- case PCI_DEVICE_ID_INTEL_B43_HB:
- case PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB:
- case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB:
- case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB:
- case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB:
- *gtt_offset = *gtt_size = MB(2);
- break;
- case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB:
- case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB:
- case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB:
- *gtt_offset = MB(2);
+ pci_read_config_dword(intel_private.pcidev,
+ I915_PTEADDR, &gtt_addr);
+ intel_private.gtt_bus_addr = gtt_addr;
+ } else {
+ u32 gtt_offset;
- pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl);
- switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) {
- default:
- case SNB_GTT_SIZE_0M:
- printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl);
- *gtt_size = MB(0);
+ switch (INTEL_GTT_GEN) {
+ case 5:
+ case 6:
+ gtt_offset = MB(2);
break;
- case SNB_GTT_SIZE_1M:
- *gtt_size = MB(1);
- break;
- case SNB_GTT_SIZE_2M:
- *gtt_size = MB(2);
+ case 4:
+ default:
+ gtt_offset = KB(512);
break;
}
- break;
- default:
- *gtt_offset = *gtt_size = KB(512);
+ intel_private.gtt_bus_addr = reg_addr + gtt_offset;
}
-}
-
-/* The intel i965 automatically initializes the agp aperture during POST.
- * Use the memory already set aside for in the GTT.
- */
-static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge)
-{
- int page_order;
- struct aper_size_info_fixed *size;
- int num_entries;
- u32 temp;
- int gtt_offset, gtt_size;
-
- size = agp_bridge->current_size;
- page_order = size->page_order;
- num_entries = size->num_entries;
- agp_bridge->gatt_table_real = NULL;
-
- pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp);
-
- temp &= 0xfff00000;
-
- intel_i965_get_gtt_range(&gtt_offset, &gtt_size);
- intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size);
+ intel_private.pte_bus_addr =
+ readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000;
- if (!intel_private.gtt)
- return -ENOMEM;
-
- intel_private.gtt_total_size = gtt_size / 4;
-
- intel_private.registers = ioremap(temp, 128 * 4096);
- if (!intel_private.registers) {
- iounmap(intel_private.gtt);
- return -ENOMEM;
- }
-
- temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000;
- global_cache_flush(); /* FIXME: ? */
-
- /* we have to call this as early as possible after the MMIO base address is known */
- intel_i830_init_gtt_entries();
- if (intel_private.gtt_entries == 0) {
- iounmap(intel_private.gtt);
- iounmap(intel_private.registers);
- return -ENOMEM;
- }
-
- agp_bridge->gatt_table = NULL;
-
- agp_bridge->gatt_bus_addr = temp;
+ intel_i9xx_setup_flush();
return 0;
}
@@ -1475,7 +1284,7 @@ static const struct agp_bridge_driver intel_810_driver = {
.cleanup = intel_i810_cleanup,
.mask_memory = intel_i810_mask_memory,
.masks = intel_i810_masks,
- .agp_enable = intel_i810_agp_enable,
+ .agp_enable = intel_fake_agp_enable,
.cache_flush = global_cache_flush,
.create_gatt_table = agp_generic_create_gatt_table,
.free_gatt_table = agp_generic_free_gatt_table,
@@ -1490,161 +1299,282 @@ static const struct agp_bridge_driver intel_810_driver = {
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
};
-static const struct agp_bridge_driver intel_830_driver = {
+static const struct agp_bridge_driver intel_fake_agp_driver = {
.owner = THIS_MODULE,
- .aperture_sizes = intel_i830_sizes,
.size_type = FIXED_APER_SIZE,
- .num_aperture_sizes = 4,
- .needs_scratch_page = true,
- .configure = intel_i830_configure,
- .fetch_size = intel_i830_fetch_size,
- .cleanup = intel_i830_cleanup,
- .mask_memory = intel_i810_mask_memory,
- .masks = intel_i810_masks,
- .agp_enable = intel_i810_agp_enable,
+ .aperture_sizes = intel_fake_agp_sizes,
+ .num_aperture_sizes = ARRAY_SIZE(intel_fake_agp_sizes),
+ .configure = intel_fake_agp_configure,
+ .fetch_size = intel_fake_agp_fetch_size,
+ .cleanup = intel_gtt_cleanup,
+ .agp_enable = intel_fake_agp_enable,
.cache_flush = global_cache_flush,
- .create_gatt_table = intel_i830_create_gatt_table,
- .free_gatt_table = intel_i830_free_gatt_table,
- .insert_memory = intel_i830_insert_entries,
- .remove_memory = intel_i830_remove_entries,
- .alloc_by_type = intel_i830_alloc_by_type,
+ .create_gatt_table = intel_fake_agp_create_gatt_table,
+ .free_gatt_table = intel_fake_agp_free_gatt_table,
+ .insert_memory = intel_fake_agp_insert_entries,
+ .remove_memory = intel_fake_agp_remove_entries,
+ .alloc_by_type = intel_fake_agp_alloc_by_type,
.free_by_type = intel_i810_free_by_type,
.agp_alloc_page = agp_generic_alloc_page,
.agp_alloc_pages = agp_generic_alloc_pages,
.agp_destroy_page = agp_generic_destroy_page,
.agp_destroy_pages = agp_generic_destroy_pages,
- .agp_type_to_mask_type = intel_i830_type_to_mask_type,
- .chipset_flush = intel_i830_chipset_flush,
+ .chipset_flush = intel_fake_agp_chipset_flush,
};
-static const struct agp_bridge_driver intel_915_driver = {
- .owner = THIS_MODULE,
- .aperture_sizes = intel_i830_sizes,
- .size_type = FIXED_APER_SIZE,
- .num_aperture_sizes = 4,
- .needs_scratch_page = true,
- .configure = intel_i9xx_configure,
- .fetch_size = intel_i9xx_fetch_size,
- .cleanup = intel_i915_cleanup,
- .mask_memory = intel_i810_mask_memory,
- .masks = intel_i810_masks,
- .agp_enable = intel_i810_agp_enable,
- .cache_flush = global_cache_flush,
- .create_gatt_table = intel_i915_create_gatt_table,
- .free_gatt_table = intel_i830_free_gatt_table,
- .insert_memory = intel_i915_insert_entries,
- .remove_memory = intel_i915_remove_entries,
- .alloc_by_type = intel_i830_alloc_by_type,
- .free_by_type = intel_i810_free_by_type,
- .agp_alloc_page = agp_generic_alloc_page,
- .agp_alloc_pages = agp_generic_alloc_pages,
- .agp_destroy_page = agp_generic_destroy_page,
- .agp_destroy_pages = agp_generic_destroy_pages,
- .agp_type_to_mask_type = intel_i830_type_to_mask_type,
- .chipset_flush = intel_i915_chipset_flush,
-#ifdef USE_PCI_DMA_API
- .agp_map_page = intel_agp_map_page,
- .agp_unmap_page = intel_agp_unmap_page,
- .agp_map_memory = intel_agp_map_memory,
- .agp_unmap_memory = intel_agp_unmap_memory,
-#endif
+static const struct intel_gtt_driver i81x_gtt_driver = {
+ .gen = 1,
+ .dma_mask_size = 32,
};
-
-static const struct agp_bridge_driver intel_i965_driver = {
- .owner = THIS_MODULE,
- .aperture_sizes = intel_i830_sizes,
- .size_type = FIXED_APER_SIZE,
- .num_aperture_sizes = 4,
- .needs_scratch_page = true,
- .configure = intel_i9xx_configure,
- .fetch_size = intel_i9xx_fetch_size,
- .cleanup = intel_i915_cleanup,
- .mask_memory = intel_i965_mask_memory,
- .masks = intel_i810_masks,
- .agp_enable = intel_i810_agp_enable,
- .cache_flush = global_cache_flush,
- .create_gatt_table = intel_i965_create_gatt_table,
- .free_gatt_table = intel_i830_free_gatt_table,
- .insert_memory = intel_i915_insert_entries,
- .remove_memory = intel_i915_remove_entries,
- .alloc_by_type = intel_i830_alloc_by_type,
- .free_by_type = intel_i810_free_by_type,
- .agp_alloc_page = agp_generic_alloc_page,
- .agp_alloc_pages = agp_generic_alloc_pages,
- .agp_destroy_page = agp_generic_destroy_page,
- .agp_destroy_pages = agp_generic_destroy_pages,
- .agp_type_to_mask_type = intel_i830_type_to_mask_type,
- .chipset_flush = intel_i915_chipset_flush,
-#ifdef USE_PCI_DMA_API
- .agp_map_page = intel_agp_map_page,
- .agp_unmap_page = intel_agp_unmap_page,
- .agp_map_memory = intel_agp_map_memory,
- .agp_unmap_memory = intel_agp_unmap_memory,
-#endif
+static const struct intel_gtt_driver i8xx_gtt_driver = {
+ .gen = 2,
+ .setup = i830_setup,
+ .cleanup = i830_cleanup,
+ .write_entry = i830_write_entry,
+ .dma_mask_size = 32,
+ .check_flags = i830_check_flags,
+ .chipset_flush = i830_chipset_flush,
};
-
-static const struct agp_bridge_driver intel_gen6_driver = {
- .owner = THIS_MODULE,
- .aperture_sizes = intel_i830_sizes,
- .size_type = FIXED_APER_SIZE,
- .num_aperture_sizes = 4,
- .needs_scratch_page = true,
- .configure = intel_i9xx_configure,
- .fetch_size = intel_i9xx_fetch_size,
- .cleanup = intel_i915_cleanup,
- .mask_memory = intel_gen6_mask_memory,
- .masks = intel_gen6_masks,
- .agp_enable = intel_i810_agp_enable,
- .cache_flush = global_cache_flush,
- .create_gatt_table = intel_i965_create_gatt_table,
- .free_gatt_table = intel_i830_free_gatt_table,
- .insert_memory = intel_i915_insert_entries,
- .remove_memory = intel_i915_remove_entries,
- .alloc_by_type = intel_i830_alloc_by_type,
- .free_by_type = intel_i810_free_by_type,
- .agp_alloc_page = agp_generic_alloc_page,
- .agp_alloc_pages = agp_generic_alloc_pages,
- .agp_destroy_page = agp_generic_destroy_page,
- .agp_destroy_pages = agp_generic_destroy_pages,
- .agp_type_to_mask_type = intel_gen6_type_to_mask_type,
- .chipset_flush = intel_i915_chipset_flush,
-#ifdef USE_PCI_DMA_API
- .agp_map_page = intel_agp_map_page,
- .agp_unmap_page = intel_agp_unmap_page,
- .agp_map_memory = intel_agp_map_memory,
- .agp_unmap_memory = intel_agp_unmap_memory,
-#endif
+static const struct intel_gtt_driver i915_gtt_driver = {
+ .gen = 3,
+ .setup = i9xx_setup,
+ .cleanup = i9xx_cleanup,
+ /* i945 is the last gpu to need phys mem (for overlay and cursors). */
+ .write_entry = i830_write_entry,
+ .dma_mask_size = 32,
+ .check_flags = i830_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
+static const struct intel_gtt_driver g33_gtt_driver = {
+ .gen = 3,
+ .is_g33 = 1,
+ .setup = i9xx_setup,
+ .cleanup = i9xx_cleanup,
+ .write_entry = i965_write_entry,
+ .dma_mask_size = 36,
+ .check_flags = i830_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
+static const struct intel_gtt_driver pineview_gtt_driver = {
+ .gen = 3,
+ .is_pineview = 1, .is_g33 = 1,
+ .setup = i9xx_setup,
+ .cleanup = i9xx_cleanup,
+ .write_entry = i965_write_entry,
+ .dma_mask_size = 36,
+ .check_flags = i830_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
+static const struct intel_gtt_driver i965_gtt_driver = {
+ .gen = 4,
+ .setup = i9xx_setup,
+ .cleanup = i9xx_cleanup,
+ .write_entry = i965_write_entry,
+ .dma_mask_size = 36,
+ .check_flags = i830_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
+static const struct intel_gtt_driver g4x_gtt_driver = {
+ .gen = 5,
+ .setup = i9xx_setup,
+ .cleanup = i9xx_cleanup,
+ .write_entry = i965_write_entry,
+ .dma_mask_size = 36,
+ .check_flags = i830_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
+static const struct intel_gtt_driver ironlake_gtt_driver = {
+ .gen = 5,
+ .is_ironlake = 1,
+ .setup = i9xx_setup,
+ .cleanup = i9xx_cleanup,
+ .write_entry = i965_write_entry,
+ .dma_mask_size = 36,
+ .check_flags = i830_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
+static const struct intel_gtt_driver sandybridge_gtt_driver = {
+ .gen = 6,
+ .setup = i9xx_setup,
+ .cleanup = gen6_cleanup,
+ .write_entry = gen6_write_entry,
+ .dma_mask_size = 40,
+ .check_flags = gen6_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
};
-static const struct agp_bridge_driver intel_g33_driver = {
- .owner = THIS_MODULE,
- .aperture_sizes = intel_i830_sizes,
- .size_type = FIXED_APER_SIZE,
- .num_aperture_sizes = 4,
- .needs_scratch_page = true,
- .configure = intel_i9xx_configure,
- .fetch_size = intel_i9xx_fetch_size,
- .cleanup = intel_i915_cleanup,
- .mask_memory = intel_i965_mask_memory,
- .masks = intel_i810_masks,
- .agp_enable = intel_i810_agp_enable,
- .cache_flush = global_cache_flush,
- .create_gatt_table = intel_i915_create_gatt_table,
- .free_gatt_table = intel_i830_free_gatt_table,
- .insert_memory = intel_i915_insert_entries,
- .remove_memory = intel_i915_remove_entries,
- .alloc_by_type = intel_i830_alloc_by_type,
- .free_by_type = intel_i810_free_by_type,
- .agp_alloc_page = agp_generic_alloc_page,
- .agp_alloc_pages = agp_generic_alloc_pages,
- .agp_destroy_page = agp_generic_destroy_page,
- .agp_destroy_pages = agp_generic_destroy_pages,
- .agp_type_to_mask_type = intel_i830_type_to_mask_type,
- .chipset_flush = intel_i915_chipset_flush,
-#ifdef USE_PCI_DMA_API
- .agp_map_page = intel_agp_map_page,
- .agp_unmap_page = intel_agp_unmap_page,
- .agp_map_memory = intel_agp_map_memory,
- .agp_unmap_memory = intel_agp_unmap_memory,
-#endif
+/* Table to describe Intel GMCH and AGP/PCIE GART drivers. At least one of
+ * driver and gmch_driver must be non-null, and find_gmch will determine
+ * which one should be used if a gmch_chip_id is present.
+ */
+static const struct intel_gtt_driver_description {
+ unsigned int gmch_chip_id;
+ char *name;
+ const struct agp_bridge_driver *gmch_driver;
+ const struct intel_gtt_driver *gtt_driver;
+} intel_gtt_chipsets[] = {
+ { PCI_DEVICE_ID_INTEL_82810_IG1, "i810", &intel_810_driver,
+ &i81x_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82810_IG3, "i810", &intel_810_driver,
+ &i81x_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82810E_IG, "i810", &intel_810_driver,
+ &i81x_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82815_CGC, "i815", &intel_810_driver,
+ &i81x_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82830_CGC, "830M",
+ &intel_fake_agp_driver, &i8xx_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82845G_IG, "830M",
+ &intel_fake_agp_driver, &i8xx_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82854_IG, "854",
+ &intel_fake_agp_driver, &i8xx_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82855GM_IG, "855GM",
+ &intel_fake_agp_driver, &i8xx_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_82865_IG, "865",
+ &intel_fake_agp_driver, &i8xx_gtt_driver},
+ { PCI_DEVICE_ID_INTEL_E7221_IG, "E7221 (i915)",
+ &intel_fake_agp_driver, &i915_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82915G_IG, "915G",
+ &intel_fake_agp_driver, &i915_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82915GM_IG, "915GM",
+ &intel_fake_agp_driver, &i915_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82945G_IG, "945G",
+ &intel_fake_agp_driver, &i915_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82945GM_IG, "945GM",
+ &intel_fake_agp_driver, &i915_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82945GME_IG, "945GME",
+ &intel_fake_agp_driver, &i915_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82946GZ_IG, "946GZ",
+ &intel_fake_agp_driver, &i965_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82G35_IG, "G35",
+ &intel_fake_agp_driver, &i965_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82965Q_IG, "965Q",
+ &intel_fake_agp_driver, &i965_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82965G_IG, "965G",
+ &intel_fake_agp_driver, &i965_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82965GM_IG, "965GM",
+ &intel_fake_agp_driver, &i965_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_82965GME_IG, "965GME/GLE",
+ &intel_fake_agp_driver, &i965_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_G33_IG, "G33",
+ &intel_fake_agp_driver, &g33_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_Q35_IG, "Q35",
+ &intel_fake_agp_driver, &g33_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_Q33_IG, "Q33",
+ &intel_fake_agp_driver, &g33_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, "GMA3150",
+ &intel_fake_agp_driver, &pineview_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_PINEVIEW_IG, "GMA3150",
+ &intel_fake_agp_driver, &pineview_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_GM45_IG, "GM45",
+ &intel_fake_agp_driver, &g4x_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, "Eaglelake",
+ &intel_fake_agp_driver, &g4x_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_Q45_IG, "Q45/Q43",
+ &intel_fake_agp_driver, &g4x_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_G45_IG, "G45/G43",
+ &intel_fake_agp_driver, &g4x_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_B43_IG, "B43",
+ &intel_fake_agp_driver, &g4x_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_B43_1_IG, "B43",
+ &intel_fake_agp_driver, &g4x_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_G41_IG, "G41",
+ &intel_fake_agp_driver, &g4x_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG,
+ "HD Graphics", &intel_fake_agp_driver, &ironlake_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG,
+ "HD Graphics", &intel_fake_agp_driver, &ironlake_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG,
+ "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG,
+ "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG,
+ "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG,
+ "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG,
+ "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG,
+ "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG,
+ "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver },
+ { 0, NULL, NULL }
};
+
+static int find_gmch(u16 device)
+{
+ struct pci_dev *gmch_device;
+
+ gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL);
+ if (gmch_device && PCI_FUNC(gmch_device->devfn) != 0) {
+ gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL,
+ device, gmch_device);
+ }
+
+ if (!gmch_device)
+ return 0;
+
+ intel_private.pcidev = gmch_device;
+ return 1;
+}
+
+int intel_gmch_probe(struct pci_dev *pdev,
+ struct agp_bridge_data *bridge)
+{
+ int i, mask;
+ bridge->driver = NULL;
+
+ for (i = 0; intel_gtt_chipsets[i].name != NULL; i++) {
+ if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) {
+ bridge->driver =
+ intel_gtt_chipsets[i].gmch_driver;
+ intel_private.driver =
+ intel_gtt_chipsets[i].gtt_driver;
+ break;
+ }
+ }
+
+ if (!bridge->driver)
+ return 0;
+
+ bridge->dev_private_data = &intel_private;
+ bridge->dev = pdev;
+
+ intel_private.bridge_dev = pci_dev_get(pdev);
+
+ dev_info(&pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name);
+
+ mask = intel_private.driver->dma_mask_size;
+ if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask)))
+ dev_err(&intel_private.pcidev->dev,
+ "set gfx device dma mask %d-bit failed!\n", mask);
+ else
+ pci_set_consistent_dma_mask(intel_private.pcidev,
+ DMA_BIT_MASK(mask));
+
+ if (bridge->driver == &intel_810_driver)
+ return 1;
+
+ if (intel_gtt_init() != 0)
+ return 0;
+
+ return 1;
+}
+EXPORT_SYMBOL(intel_gmch_probe);
+
+struct intel_gtt *intel_gtt_get(void)
+{
+ return &intel_private.base;
+}
+EXPORT_SYMBOL(intel_gtt_get);
+
+void intel_gmch_remove(struct pci_dev *pdev)
+{
+ if (intel_private.pcidev)
+ pci_dev_put(intel_private.pcidev);
+ if (intel_private.bridge_dev)
+ pci_dev_put(intel_private.bridge_dev);
+}
+EXPORT_SYMBOL(intel_gmch_remove);
+
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index a4eee324eb1e..55b8667f739f 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -32,12 +32,12 @@
#include <linux/bitops.h>
#include <linux/compat.h>
#include <linux/clocksource.h>
+#include <linux/uaccess.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <asm/current.h>
-#include <asm/uaccess.h>
#include <asm/system.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <asm/div64.h>
@@ -81,13 +81,13 @@ static cycle_t read_hpet(struct clocksource *cs)
}
static struct clocksource clocksource_hpet = {
- .name = "hpet",
- .rating = 250,
- .read = read_hpet,
- .mask = CLOCKSOURCE_MASK(64),
- .mult = 0, /* to be calculated */
- .shift = 10,
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .name = "hpet",
+ .rating = 250,
+ .read = read_hpet,
+ .mask = CLOCKSOURCE_MASK(64),
+ .mult = 0, /* to be calculated */
+ .shift = 10,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct clocksource *hpet_clocksource;
#endif
@@ -465,6 +465,21 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
if (irq) {
unsigned long irq_flags;
+ if (devp->hd_flags & HPET_SHARED_IRQ) {
+ /*
+ * To prevent the interrupt handler from seeing an
+ * unwanted interrupt status bit, program the timer
+ * so that it will not fire in the near future ...
+ */
+ writel(readl(&timer->hpet_config) & ~Tn_TYPE_CNF_MASK,
+ &timer->hpet_config);
+ write_counter(read_counter(&hpet->hpet_mc),
+ &timer->hpet_compare);
+ /* ... and clear any left-over status. */
+ isr = 1 << (devp - devp->hd_hpets->hp_dev);
+ writel(isr, &hpet->hpet_isr);
+ }
+
sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
irq_flags = devp->hd_flags & HPET_SHARED_IRQ
? IRQF_SHARED : IRQF_DISABLED;
@@ -581,11 +596,10 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
break;
case HPET_INFO:
{
+ memset(info, 0, sizeof(*info));
if (devp->hd_ireqfreq)
info->hi_ireqfreq =
hpet_time_div(hpetp, devp->hd_ireqfreq);
- else
- info->hi_ireqfreq = 0;
info->hi_flags =
readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK;
info->hi_hpet = hpetp->hp_which;
@@ -811,7 +825,7 @@ int hpet_alloc(struct hpet_data *hdp)
struct hpets *hpetp;
size_t siz;
struct hpet __iomem *hpet;
- static struct hpets *last = NULL;
+ static struct hpets *last;
unsigned long period;
unsigned long long temp;
u32 remainder;
@@ -1000,6 +1014,8 @@ static int hpet_acpi_add(struct acpi_device *device)
return -ENODEV;
if (!data.hd_address || !data.hd_nirqs) {
+ if (data.hd_address)
+ iounmap(data.hd_address);
printk("%s: no address or irqs in _CRS\n", __func__);
return -ENODEV;
}
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index 7b01bc609de3..c3425bb3a1f6 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -1303,13 +1303,11 @@ static int __init hvc_iucv_init(void)
if (rc) {
pr_err("Registering IUCV handlers failed with error code=%d\n",
rc);
- goto out_error_iucv;
+ goto out_error_hvc;
}
return 0;
-out_error_iucv:
- iucv_unregister(&hvc_iucv_handler, 0);
out_error_hvc:
for (i = 0; i < hvc_iucv_devices; i++)
if (hvc_iucv_table[i])
diff --git a/drivers/char/hvc_tile.c b/drivers/char/hvc_tile.c
index c4efb55cbc03..7a84a0595477 100644
--- a/drivers/char/hvc_tile.c
+++ b/drivers/char/hvc_tile.c
@@ -61,7 +61,8 @@ console_initcall(hvc_tile_console_init);
static int __init hvc_tile_init(void)
{
- hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128);
- return 0;
+ struct hvc_struct *s;
+ s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128);
+ return IS_ERR(s) ? PTR_ERR(s) : 0;
}
device_initcall(hvc_tile_init);
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index 60446f82a3fc..6b8e6d18a8e6 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -74,7 +74,8 @@ static int __write_console(const char *data, int len)
wmb(); /* write ring before updating pointer */
intf->out_prod = prod;
- notify_daemon();
+ if (sent)
+ notify_daemon();
return sent;
}
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index e537610d2f09..b293d57d30a7 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -1665,6 +1665,17 @@ static int check_hotmod_int_op(const char *curr, const char *option,
return 0;
}
+static struct smi_info *smi_info_alloc(void)
+{
+ struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
+
+ if (info) {
+ spin_lock_init(&info->si_lock);
+ spin_lock_init(&info->msg_lock);
+ }
+ return info;
+}
+
static int hotmod_handler(const char *val, struct kernel_param *kp)
{
char *str = kstrdup(val, GFP_KERNEL);
@@ -1779,7 +1790,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
}
if (op == HM_ADD) {
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info) {
rv = -ENOMEM;
goto out;
@@ -1844,7 +1855,7 @@ static __devinit void hardcode_find_bmc(void)
if (!ports[i] && !addrs[i])
continue;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info)
return;
@@ -2027,7 +2038,7 @@ static __devinit int try_init_spmi(struct SPMITable *spmi)
return -ENODEV;
}
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info) {
printk(KERN_ERR PFX "Could not allocate SI data (3)\n");
return -ENOMEM;
@@ -2137,7 +2148,7 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
if (!acpi_dev)
return -ENODEV;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info)
return -ENOMEM;
@@ -2318,7 +2329,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
{
struct smi_info *info;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info) {
printk(KERN_ERR PFX "Could not allocate SI data\n");
return;
@@ -2425,7 +2436,7 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
struct smi_info *info;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info)
return -ENOMEM;
@@ -2566,7 +2577,7 @@ static int __devinit ipmi_of_probe(struct platform_device *dev,
return -EINVAL;
}
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info) {
dev_err(&dev->dev,
@@ -3013,7 +3024,7 @@ static __devinit void default_find_bmc(void)
if (check_legacy_ioport(ipmi_defaults[i].port))
continue;
#endif
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = smi_info_alloc();
if (!info)
return;
@@ -3138,9 +3149,6 @@ static int try_smi_init(struct smi_info *new_smi)
goto out_err;
}
- spin_lock_init(&(new_smi->si_lock));
- spin_lock_init(&(new_smi->msg_lock));
-
/* Do low-level detection first. */
if (new_smi->handlers->detect(new_smi->si_sm)) {
if (new_smi->addr_source)
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index a7ca75212bfe..e95d7876ca6b 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -175,8 +175,7 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
*/
struct getset_keycode_data {
- unsigned int scancode;
- unsigned int keycode;
+ struct input_keymap_entry ke;
int error;
};
@@ -184,32 +183,50 @@ static int getkeycode_helper(struct input_handle *handle, void *data)
{
struct getset_keycode_data *d = data;
- d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode);
+ d->error = input_get_keycode(handle->dev, &d->ke);
return d->error == 0; /* stop as soon as we successfully get one */
}
int getkeycode(unsigned int scancode)
{
- struct getset_keycode_data d = { scancode, 0, -ENODEV };
+ struct getset_keycode_data d = {
+ .ke = {
+ .flags = 0,
+ .len = sizeof(scancode),
+ .keycode = 0,
+ },
+ .error = -ENODEV,
+ };
+
+ memcpy(d.ke.scancode, &scancode, sizeof(scancode));
input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
- return d.error ?: d.keycode;
+ return d.error ?: d.ke.keycode;
}
static int setkeycode_helper(struct input_handle *handle, void *data)
{
struct getset_keycode_data *d = data;
- d->error = input_set_keycode(handle->dev, d->scancode, d->keycode);
+ d->error = input_set_keycode(handle->dev, &d->ke);
return d->error == 0; /* stop as soon as we successfully set one */
}
int setkeycode(unsigned int scancode, unsigned int keycode)
{
- struct getset_keycode_data d = { scancode, keycode, -ENODEV };
+ struct getset_keycode_data d = {
+ .ke = {
+ .flags = 0,
+ .len = sizeof(scancode),
+ .keycode = keycode,
+ },
+ .error = -ENODEV,
+ };
+
+ memcpy(d.ke.scancode, &scancode, sizeof(scancode));
input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index e985b1c2730e..1256454b2d43 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -876,6 +876,10 @@ static int memory_open(struct inode *inode, struct file *filp)
if (dev->dev_info)
filp->f_mapping->backing_dev_info = dev->dev_info;
+ /* Is /dev/mem or /dev/kmem ? */
+ if (dev->dev_info == &directly_mappable_cdev_bdi)
+ filp->f_mode |= FMODE_UNSIGNED_OFFSET;
+
if (dev->fops->open)
return dev->fops->open(inode, filp);
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index f3019f53e875..eaa5d3efa79d 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -566,10 +566,16 @@ static const unsigned char sysrq_xlate[KEY_MAX + 1] =
static bool sysrq_down;
static int sysrq_alt_use;
static int sysrq_alt;
+static DEFINE_SPINLOCK(sysrq_event_lock);
static bool sysrq_filter(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
+ bool suppress;
+
+ /* We are called with interrupts disabled, just take the lock */
+ spin_lock(&sysrq_event_lock);
+
if (type != EV_KEY)
goto out;
@@ -601,7 +607,10 @@ static bool sysrq_filter(struct input_handle *handle, unsigned int type,
}
out:
- return sysrq_down;
+ suppress = sysrq_down;
+ spin_unlock(&sysrq_event_lock);
+
+ return suppress;
}
static int sysrq_connect(struct input_handler *handler,
@@ -652,8 +661,8 @@ static void sysrq_disconnect(struct input_handle *handle)
}
/*
- * We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all
- * keyboards have SysRq ikey predefined and so user may add it to keymap
+ * We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all
+ * keyboards have SysRq key predefined and so user may add it to keymap
* later, but we expect all such keyboards to have left alt.
*/
static const struct input_device_id sysrq_ids[] = {
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 717305d30444..a44611652282 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -308,7 +308,7 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id)
* isr before we end up here.
*/
if (p->flags & FLAG_CLOCKSOURCE)
- p->total_cycles += p->match_value;
+ p->total_cycles += p->match_value + 1;
if (!(p->flags & FLAG_REPROGRAM))
p->next_match_value = p->max_match_value;
@@ -403,7 +403,7 @@ static cycle_t sh_cmt_clocksource_read(struct clocksource *cs)
raw = sh_cmt_get_counter(p, &has_wrapped);
if (unlikely(has_wrapped))
- raw += p->match_value;
+ raw += p->match_value + 1;
spin_unlock_irqrestore(&p->lock, flags);
return value + raw;
@@ -445,7 +445,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
/* clk_get_rate() needs an enabled clock */
clk_enable(p->clk);
- p->rate = clk_get_rate(p->clk) / (p->width == 16) ? 512 : 8;
+ p->rate = clk_get_rate(p->clk) / ((p->width == 16) ? 512 : 8);
clk_disable(p->clk);
/* TODO: calculate good shift from rate and counter bit width */
@@ -478,7 +478,7 @@ static void sh_cmt_clock_event_start(struct sh_cmt_priv *p, int periodic)
ced->min_delta_ns = clockevent_delta2ns(0x1f, ced);
if (periodic)
- sh_cmt_set_next(p, (p->rate + HZ/2) / HZ);
+ sh_cmt_set_next(p, ((p->rate + HZ/2) / HZ) - 1);
else
sh_cmt_set_next(p, p->max_match_value);
}
@@ -523,9 +523,9 @@ static int sh_cmt_clock_event_next(unsigned long delta,
BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
if (likely(p->flags & FLAG_IRQCONTEXT))
- p->next_match_value = delta;
+ p->next_match_value = delta - 1;
else
- sh_cmt_set_next(p, delta);
+ sh_cmt_set_next(p, delta - 1);
return 0;
}
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 199dcb9f0b83..c63a43823744 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -918,8 +918,8 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
spin_lock_irqsave(&cpufreq_driver_lock, flags);
for_each_cpu(j, policy->cpus) {
- if (!cpu_online(j))
- continue;
+ if (!cpu_online(j))
+ continue;
per_cpu(cpufreq_cpu_data, j) = policy;
per_cpu(cpufreq_policy_cpu, j) = policy->cpu;
}
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 7b5093664e49..c631f27a3dcc 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -30,6 +30,8 @@
#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10)
#define DEF_FREQUENCY_UP_THRESHOLD (80)
+#define DEF_SAMPLING_DOWN_FACTOR (1)
+#define MAX_SAMPLING_DOWN_FACTOR (100000)
#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3)
#define MICRO_FREQUENCY_UP_THRESHOLD (95)
#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000)
@@ -82,6 +84,7 @@ struct cpu_dbs_info_s {
unsigned int freq_lo;
unsigned int freq_lo_jiffies;
unsigned int freq_hi_jiffies;
+ unsigned int rate_mult;
int cpu;
unsigned int sample_type:1;
/*
@@ -108,10 +111,12 @@ static struct dbs_tuners {
unsigned int up_threshold;
unsigned int down_differential;
unsigned int ignore_nice;
+ unsigned int sampling_down_factor;
unsigned int powersave_bias;
unsigned int io_is_busy;
} dbs_tuners_ins = {
.up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
+ .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR,
.down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL,
.ignore_nice = 0,
.powersave_bias = 0,
@@ -259,6 +264,7 @@ static ssize_t show_##file_name \
show_one(sampling_rate, sampling_rate);
show_one(io_is_busy, io_is_busy);
show_one(up_threshold, up_threshold);
+show_one(sampling_down_factor, sampling_down_factor);
show_one(ignore_nice_load, ignore_nice);
show_one(powersave_bias, powersave_bias);
@@ -340,6 +346,29 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b,
return count;
}
+static ssize_t store_sampling_down_factor(struct kobject *a,
+ struct attribute *b, const char *buf, size_t count)
+{
+ unsigned int input, j;
+ int ret;
+ ret = sscanf(buf, "%u", &input);
+
+ if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
+ return -EINVAL;
+ mutex_lock(&dbs_mutex);
+ dbs_tuners_ins.sampling_down_factor = input;
+
+ /* Reset down sampling multiplier in case it was active */
+ for_each_online_cpu(j) {
+ struct cpu_dbs_info_s *dbs_info;
+ dbs_info = &per_cpu(od_cpu_dbs_info, j);
+ dbs_info->rate_mult = 1;
+ }
+ mutex_unlock(&dbs_mutex);
+
+ return count;
+}
+
static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
@@ -401,6 +430,7 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b,
define_one_global_rw(sampling_rate);
define_one_global_rw(io_is_busy);
define_one_global_rw(up_threshold);
+define_one_global_rw(sampling_down_factor);
define_one_global_rw(ignore_nice_load);
define_one_global_rw(powersave_bias);
@@ -409,6 +439,7 @@ static struct attribute *dbs_attributes[] = {
&sampling_rate_min.attr,
&sampling_rate.attr,
&up_threshold.attr,
+ &sampling_down_factor.attr,
&ignore_nice_load.attr,
&powersave_bias.attr,
&io_is_busy.attr,
@@ -562,6 +593,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
/* Check for frequency increase */
if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) {
+ /* If switching to max speed, apply sampling_down_factor */
+ if (policy->cur < policy->max)
+ this_dbs_info->rate_mult =
+ dbs_tuners_ins.sampling_down_factor;
dbs_freq_increase(policy, policy->max);
return;
}
@@ -584,6 +619,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
(dbs_tuners_ins.up_threshold -
dbs_tuners_ins.down_differential);
+ /* No longer fully busy, reset rate_mult */
+ this_dbs_info->rate_mult = 1;
+
if (freq_next < policy->min)
freq_next = policy->min;
@@ -607,7 +645,8 @@ static void do_dbs_timer(struct work_struct *work)
int sample_type = dbs_info->sample_type;
/* We want all CPUs to do sampling nearly on same jiffy */
- int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+ int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate
+ * dbs_info->rate_mult);
if (num_online_cpus() > 1)
delay -= jiffies % delay;
@@ -711,6 +750,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
}
}
this_dbs_info->cpu = cpu;
+ this_dbs_info->rate_mult = 1;
ondemand_powersave_bias_init_cpu(cpu);
/*
* Start the timerschedule work, when this governor
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index 0eac3da566ba..a84250a5dd51 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -1467,7 +1467,7 @@ static int ablkcipher_add(unsigned int *drestp, struct scatterlist *dst,
return -EINVAL;
while (size) {
- copy = min(drest, min(size, dst->length));
+ copy = min3(drest, size, dst->length);
size -= copy;
drest -= copy;
@@ -1729,7 +1729,7 @@ static int ablkcipher_get(void *saddr, unsigned int *srestp, unsigned int offset
return -EINVAL;
while (size) {
- copy = min(srest, min(dst->length, size));
+ copy = min3(srest, dst->length, size);
daddr = kmap_atomic(sg_page(dst), KM_IRQ0);
memcpy(daddr + dst->offset + offset, saddr, copy);
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index ce7146677e9b..d7ca43a828bd 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -42,8 +42,10 @@
#if PAGE_SHIFT < 20
#define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) )
+#define MiB_TO_PAGES(mb) ((mb) >> (20 - PAGE_SHIFT))
#else /* PAGE_SHIFT > 20 */
#define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) )
+#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20))
#endif
#define edac_printk(level, prefix, fmt, arg...) \
@@ -328,7 +330,7 @@ struct csrow_info {
struct mcidev_sysfs_group {
const char *name; /* group name */
- struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
+ const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
};
struct mcidev_sysfs_group_kobj {
@@ -336,7 +338,7 @@ struct mcidev_sysfs_group_kobj {
struct kobject kobj; /* kobj for the group */
- struct mcidev_sysfs_group *grp; /* group description table */
+ const struct mcidev_sysfs_group *grp; /* group description table */
struct mem_ctl_info *mci; /* the parent */
};
@@ -347,7 +349,7 @@ struct mcidev_sysfs_group_kobj {
struct mcidev_sysfs_attribute {
/* It should use either attr or grp */
struct attribute attr;
- struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
+ const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
/* Ops for show/store values at the attribute - not used on group */
ssize_t (*show)(struct mem_ctl_info *,char *);
@@ -440,7 +442,7 @@ struct mem_ctl_info {
* If attributes are desired, then set to array of attributes
* If no attributes are desired, leave NULL
*/
- struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes;
+ const struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes;
/* work struct for this MC */
struct delayed_work work;
@@ -810,6 +812,7 @@ extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
extern int edac_mc_add_mc(struct mem_ctl_info *mci);
extern void edac_mc_free(struct mem_ctl_info *mci);
extern struct mem_ctl_info *edac_mc_find(int idx);
+extern struct mem_ctl_info *find_mci_by_dev(struct device *dev);
extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev);
extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
unsigned long page);
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 6b21e25f7a84..ba6586a69ccc 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -207,6 +207,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
}
mci->op_state = OP_ALLOC;
+ INIT_LIST_HEAD(&mci->grp_kobj_list);
/*
* Initialize the 'root' kobj for the edac_mc controller
@@ -234,18 +235,24 @@ EXPORT_SYMBOL_GPL(edac_mc_alloc);
*/
void edac_mc_free(struct mem_ctl_info *mci)
{
+ debugf1("%s()\n", __func__);
+
edac_mc_unregister_sysfs_main_kobj(mci);
+
+ /* free the mci instance memory here */
+ kfree(mci);
}
EXPORT_SYMBOL_GPL(edac_mc_free);
-/*
+/**
* find_mci_by_dev
*
* scan list of controllers looking for the one that manages
* the 'dev' device
+ * @dev: pointer to a struct device related with the MCI
*/
-static struct mem_ctl_info *find_mci_by_dev(struct device *dev)
+struct mem_ctl_info *find_mci_by_dev(struct device *dev)
{
struct mem_ctl_info *mci;
struct list_head *item;
@@ -261,6 +268,7 @@ static struct mem_ctl_info *find_mci_by_dev(struct device *dev)
return NULL;
}
+EXPORT_SYMBOL_GPL(find_mci_by_dev);
/*
* handler for EDAC to check if NMI type handler has asserted interrupt
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index a4135860149b..dce61f7ba38b 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -631,9 +631,6 @@ static void edac_mci_control_release(struct kobject *kobj)
/* decrement the module ref count */
module_put(mci->owner);
-
- /* free the mci instance memory here */
- kfree(mci);
}
static struct kobj_type ktype_mci = {
@@ -713,6 +710,8 @@ fail_out:
*/
void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
{
+ debugf1("%s()\n", __func__);
+
/* delete the kobj from the mc_kset */
kobject_put(&mci->edac_mci_kobj);
}
@@ -760,8 +759,6 @@ static void edac_inst_grp_release(struct kobject *kobj)
grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj);
mci = grp->mci;
-
- kobject_put(&mci->edac_mci_kobj);
}
/* Intermediate show/store table */
@@ -784,7 +781,7 @@ static struct kobj_type ktype_inst_grp = {
* object tree.
*/
static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
- struct mcidev_sysfs_attribute *sysfs_attrib,
+ const struct mcidev_sysfs_attribute *sysfs_attrib,
struct kobject *kobj)
{
int err;
@@ -792,6 +789,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
debugf1("%s()\n", __func__);
while (sysfs_attrib) {
+ debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
if (sysfs_attrib->grp) {
struct mcidev_sysfs_group_kobj *grp_kobj;
@@ -799,10 +797,9 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
if (!grp_kobj)
return -ENOMEM;
- list_add_tail(&grp_kobj->list, &mci->grp_kobj_list);
-
grp_kobj->grp = sysfs_attrib->grp;
grp_kobj->mci = mci;
+ list_add_tail(&grp_kobj->list, &mci->grp_kobj_list);
debugf0("%s() grp %s, mci %p\n", __func__,
sysfs_attrib->grp->name, mci);
@@ -811,26 +808,28 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
&ktype_inst_grp,
&mci->edac_mci_kobj,
sysfs_attrib->grp->name);
- if (err)
+ if (err < 0) {
+ printk(KERN_ERR "kobject_init_and_add failed: %d\n", err);
return err;
-
+ }
err = edac_create_mci_instance_attributes(mci,
grp_kobj->grp->mcidev_attr,
&grp_kobj->kobj);
- if (err)
+ if (err < 0)
return err;
} else if (sysfs_attrib->attr.name) {
debugf0("%s() file %s\n", __func__,
sysfs_attrib->attr.name);
err = sysfs_create_file(kobj, &sysfs_attrib->attr);
+ if (err < 0) {
+ printk(KERN_ERR "sysfs_create_file failed: %d\n", err);
+ return err;
+ }
} else
break;
- if (err) {
- return err;
- }
sysfs_attrib++;
}
@@ -843,7 +842,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
* directory of this mci instance.
*/
static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
- struct mcidev_sysfs_attribute *sysfs_attrib,
+ const struct mcidev_sysfs_attribute *sysfs_attrib,
struct kobject *kobj, int count)
{
struct mcidev_sysfs_group_kobj *grp_kobj, *tmp;
@@ -855,13 +854,24 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
* Remove first all the atributes
*/
while (sysfs_attrib) {
+ debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
if (sysfs_attrib->grp) {
- list_for_each_entry(grp_kobj, &mci->grp_kobj_list,
- list)
- if (grp_kobj->grp == sysfs_attrib->grp)
+ debugf1("%s() seeking for group %s\n",
+ __func__, sysfs_attrib->grp->name);
+ list_for_each_entry(grp_kobj,
+ &mci->grp_kobj_list, list) {
+ debugf1("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp);
+ if (grp_kobj->grp == sysfs_attrib->grp) {
edac_remove_mci_instance_attributes(mci,
grp_kobj->grp->mcidev_attr,
&grp_kobj->kobj, count + 1);
+ debugf0("%s() group %s\n", __func__,
+ sysfs_attrib->grp->name);
+ kobject_put(&grp_kobj->kobj);
+ }
+ }
+ debugf1("%s() end of seeking for group %s\n",
+ __func__, sysfs_attrib->grp->name);
} else if (sysfs_attrib->attr.name) {
debugf0("%s() file %s\n", __func__,
sysfs_attrib->attr.name);
@@ -871,15 +881,14 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
sysfs_attrib++;
}
- /*
- * Now that all attributes got removed, it is save to remove all groups
- */
- if (!count)
- list_for_each_entry_safe(grp_kobj, tmp, &mci->grp_kobj_list,
- list) {
- debugf0("%s() grp %s\n", __func__, grp_kobj->grp->name);
- kobject_put(&grp_kobj->kobj);
- }
+ /* Remove the group objects */
+ if (count)
+ return;
+ list_for_each_entry_safe(grp_kobj, tmp,
+ &mci->grp_kobj_list, list) {
+ list_del(&grp_kobj->list);
+ kfree(grp_kobj);
+ }
}
@@ -971,6 +980,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
debugf0("%s()\n", __func__);
/* remove all csrow kobjects */
+ debugf0("%s() unregister this mci kobj\n", __func__);
for (i = 0; i < mci->nr_csrows; i++) {
if (mci->csrows[i].nr_pages > 0) {
debugf0("%s() unreg csrow-%d\n", __func__, i);
@@ -978,20 +988,20 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
}
}
- debugf0("%s() remove_link\n", __func__);
+ /* remove this mci instance's attribtes */
+ if (mci->mc_driver_sysfs_attributes) {
+ debugf0("%s() unregister mci private attributes\n", __func__);
+ edac_remove_mci_instance_attributes(mci,
+ mci->mc_driver_sysfs_attributes,
+ &mci->edac_mci_kobj, 0);
+ }
/* remove the symlink */
+ debugf0("%s() remove_link\n", __func__);
sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
- debugf0("%s() remove_mci_instance\n", __func__);
-
- /* remove this mci instance's attribtes */
- edac_remove_mci_instance_attributes(mci,
- mci->mc_driver_sysfs_attributes,
- &mci->edac_mci_kobj, 0);
- debugf0("%s() unregister this mci kobj\n", __func__);
-
/* unregister this instance's kobject */
+ debugf0("%s() remove_mci_instance\n", __func__);
kobject_put(&mci->edac_mci_kobj);
}
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 0fd5b85a0f75..362861c15779 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -39,6 +39,14 @@
#include "edac_core.h"
+/* Static vars */
+static LIST_HEAD(i7core_edac_list);
+static DEFINE_MUTEX(i7core_edac_lock);
+static int probed;
+
+static int use_pci_fixup;
+module_param(use_pci_fixup, int, 0444);
+MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
/*
* This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
* registers start at bus 255, and are not reported by BIOS.
@@ -212,8 +220,8 @@ struct pci_id_descr {
};
struct pci_id_table {
- struct pci_id_descr *descr;
- int n_devs;
+ const struct pci_id_descr *descr;
+ int n_devs;
};
struct i7core_dev {
@@ -235,8 +243,6 @@ struct i7core_pvt {
struct i7core_inject inject;
struct i7core_channel channel[NUM_CHANS];
- int channels; /* Number of active channels */
-
int ce_count_available;
int csrow_map[NUM_CHANS][MAX_DIMMS];
@@ -261,22 +267,22 @@ struct i7core_pvt {
/* Count indicator to show errors not got */
unsigned mce_overrun;
-};
-/* Static vars */
-static LIST_HEAD(i7core_edac_list);
-static DEFINE_MUTEX(i7core_edac_lock);
+ /* Struct to control EDAC polling */
+ struct edac_pci_ctl_info *i7core_pci;
+};
#define PCI_DESCR(device, function, device_id) \
.dev = (device), \
.func = (function), \
.dev_id = (device_id)
-struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
+static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
/* Memory controller */
{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
- /* Exists only for RDIMM */
+
+ /* Exists only for RDIMM */
{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 },
{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
@@ -297,19 +303,9 @@ struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
-
- /* Generic Non-core registers */
- /*
- * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
- * On Xeon 55xx, however, it has a different id (8086:2c40). So,
- * the probing code needs to test for the other address in case of
- * failure of this one
- */
- { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) },
-
};
-struct pci_id_descr pci_dev_descr_lynnfield[] = {
+static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
{ PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) },
{ PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) },
{ PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) },
@@ -323,15 +319,9 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = {
{ PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
{ PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
{ PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) },
-
- /*
- * This is the PCI device has an alternate address on some
- * processors like Core i7 860
- */
- { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) },
};
-struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
+static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
/* Memory controller */
{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) },
{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) },
@@ -356,17 +346,14 @@ struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) },
-
- /* Generic Non-core registers */
- { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) },
-
};
-#define PCI_ID_TABLE_ENTRY(A) { A, ARRAY_SIZE(A) }
-struct pci_id_table pci_dev_table[] = {
+#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
+static const struct pci_id_table pci_dev_table[] = {
PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
+ {0,} /* 0 terminated list. */
};
/*
@@ -378,8 +365,6 @@ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
{0,} /* 0 terminated list. */
};
-static struct edac_pci_ctl_info *i7core_pci;
-
/****************************************************************************
Anciliary status routines
****************************************************************************/
@@ -442,6 +427,36 @@ static struct i7core_dev *get_i7core_dev(u8 socket)
return NULL;
}
+static struct i7core_dev *alloc_i7core_dev(u8 socket,
+ const struct pci_id_table *table)
+{
+ struct i7core_dev *i7core_dev;
+
+ i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
+ if (!i7core_dev)
+ return NULL;
+
+ i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs,
+ GFP_KERNEL);
+ if (!i7core_dev->pdev) {
+ kfree(i7core_dev);
+ return NULL;
+ }
+
+ i7core_dev->socket = socket;
+ i7core_dev->n_devs = table->n_devs;
+ list_add_tail(&i7core_dev->list, &i7core_edac_list);
+
+ return i7core_dev;
+}
+
+static void free_i7core_dev(struct i7core_dev *i7core_dev)
+{
+ list_del(&i7core_dev->list);
+ kfree(i7core_dev->pdev);
+ kfree(i7core_dev);
+}
+
/****************************************************************************
Memory check routines
****************************************************************************/
@@ -484,7 +499,7 @@ static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
* to add a fake description for csrows.
* So, this driver is attributing one DIMM memory for one csrow.
*/
-static int i7core_get_active_channels(u8 socket, unsigned *channels,
+static int i7core_get_active_channels(const u8 socket, unsigned *channels,
unsigned *csrows)
{
struct pci_dev *pdev = NULL;
@@ -545,12 +560,13 @@ static int i7core_get_active_channels(u8 socket, unsigned *channels,
return 0;
}
-static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
+static int get_dimm_config(const struct mem_ctl_info *mci)
{
struct i7core_pvt *pvt = mci->pvt_info;
struct csrow_info *csr;
struct pci_dev *pdev;
int i, j;
+ int csrow = 0;
unsigned long last_page = 0;
enum edac_type mode;
enum mem_type mtype;
@@ -664,13 +680,9 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
RANKOFFSET(dimm_dod[j]),
banks, ranks, rows, cols);
-#if PAGE_SHIFT > 20
- npages = size >> (PAGE_SHIFT - 20);
-#else
- npages = size << (20 - PAGE_SHIFT);
-#endif
+ npages = MiB_TO_PAGES(size);
- csr = &mci->csrows[*csrow];
+ csr = &mci->csrows[csrow];
csr->first_page = last_page + 1;
last_page += npages;
csr->last_page = last_page;
@@ -678,13 +690,13 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
csr->page_mask = 0;
csr->grain = 8;
- csr->csrow_idx = *csrow;
+ csr->csrow_idx = csrow;
csr->nr_channels = 1;
csr->channels[0].chan_idx = i;
csr->channels[0].ce_count = 0;
- pvt->csrow_map[i][j] = *csrow;
+ pvt->csrow_map[i][j] = csrow;
switch (banks) {
case 4:
@@ -703,7 +715,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
csr->edac_mode = mode;
csr->mtype = mtype;
- (*csrow)++;
+ csrow++;
}
pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
@@ -736,7 +748,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
we're disabling error injection on all write calls to the sysfs nodes that
controls the error code injection.
*/
-static int disable_inject(struct mem_ctl_info *mci)
+static int disable_inject(const struct mem_ctl_info *mci)
{
struct i7core_pvt *pvt = mci->pvt_info;
@@ -921,7 +933,7 @@ DECLARE_ADDR_MATCH(bank, 32);
DECLARE_ADDR_MATCH(page, 0x10000);
DECLARE_ADDR_MATCH(col, 0x4000);
-static int write_and_test(struct pci_dev *dev, int where, u32 val)
+static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
{
u32 read;
int count;
@@ -1120,35 +1132,34 @@ DECLARE_COUNTER(2);
* Sysfs struct
*/
-
-static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
+static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
ATTR_ADDR_MATCH(channel),
ATTR_ADDR_MATCH(dimm),
ATTR_ADDR_MATCH(rank),
ATTR_ADDR_MATCH(bank),
ATTR_ADDR_MATCH(page),
ATTR_ADDR_MATCH(col),
- { .attr = { .name = NULL } }
+ { } /* End of list */
};
-static struct mcidev_sysfs_group i7core_inject_addrmatch = {
+static const struct mcidev_sysfs_group i7core_inject_addrmatch = {
.name = "inject_addrmatch",
.mcidev_attr = i7core_addrmatch_attrs,
};
-static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
+static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
ATTR_COUNTER(0),
ATTR_COUNTER(1),
ATTR_COUNTER(2),
{ .attr = { .name = NULL } }
};
-static struct mcidev_sysfs_group i7core_udimm_counters = {
+static const struct mcidev_sysfs_group i7core_udimm_counters = {
.name = "all_channel_counts",
.mcidev_attr = i7core_udimm_counters_attrs,
};
-static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
+static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = {
{
.attr = {
.name = "inject_section",
@@ -1180,8 +1191,44 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
.show = i7core_inject_enable_show,
.store = i7core_inject_enable_store,
},
- { .attr = { .name = NULL } }, /* Reserved for udimm counters */
- { .attr = { .name = NULL } }
+ { } /* End of list */
+};
+
+static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
+ {
+ .attr = {
+ .name = "inject_section",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = i7core_inject_section_show,
+ .store = i7core_inject_section_store,
+ }, {
+ .attr = {
+ .name = "inject_type",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = i7core_inject_type_show,
+ .store = i7core_inject_type_store,
+ }, {
+ .attr = {
+ .name = "inject_eccmask",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = i7core_inject_eccmask_show,
+ .store = i7core_inject_eccmask_store,
+ }, {
+ .grp = &i7core_inject_addrmatch,
+ }, {
+ .attr = {
+ .name = "inject_enable",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = i7core_inject_enable_show,
+ .store = i7core_inject_enable_store,
+ }, {
+ .grp = &i7core_udimm_counters,
+ },
+ { } /* End of list */
};
/****************************************************************************
@@ -1189,7 +1236,7 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
****************************************************************************/
/*
- * i7core_put_devices 'put' all the devices that we have
+ * i7core_put_all_devices 'put' all the devices that we have
* reserved via 'get'
*/
static void i7core_put_devices(struct i7core_dev *i7core_dev)
@@ -1206,23 +1253,23 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev)
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
pci_dev_put(pdev);
}
- kfree(i7core_dev->pdev);
- list_del(&i7core_dev->list);
- kfree(i7core_dev);
}
static void i7core_put_all_devices(void)
{
struct i7core_dev *i7core_dev, *tmp;
- list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
+ list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
i7core_put_devices(i7core_dev);
+ free_i7core_dev(i7core_dev);
+ }
}
-static void __init i7core_xeon_pci_fixup(struct pci_id_table *table)
+static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
{
struct pci_dev *pdev = NULL;
int i;
+
/*
* On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
* aren't announced by acpi. So, we need to use a legacy scan probing
@@ -1257,16 +1304,18 @@ static unsigned i7core_pci_lastbus(void)
}
/*
- * i7core_get_devices Find and perform 'get' operation on the MCH's
+ * i7core_get_all_devices Find and perform 'get' operation on the MCH's
* device/functions we want to reference for this driver
*
* Need to 'get' device 16 func 1 and func 2
*/
-int i7core_get_onedevice(struct pci_dev **prev, int devno,
- struct pci_id_descr *dev_descr, unsigned n_devs,
- unsigned last_bus)
+static int i7core_get_onedevice(struct pci_dev **prev,
+ const struct pci_id_table *table,
+ const unsigned devno,
+ const unsigned last_bus)
{
struct i7core_dev *i7core_dev;
+ const struct pci_id_descr *dev_descr = &table->descr[devno];
struct pci_dev *pdev = NULL;
u8 bus = 0;
@@ -1275,20 +1324,6 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno,
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
dev_descr->dev_id, *prev);
- /*
- * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
- * is at addr 8086:2c40, instead of 8086:2c41. So, we need
- * to probe for the alternate address in case of failure
- */
- if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev)
- pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
-
- if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev)
- pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
- *prev);
-
if (!pdev) {
if (*prev) {
*prev = pdev;
@@ -1315,18 +1350,11 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno,
i7core_dev = get_i7core_dev(socket);
if (!i7core_dev) {
- i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
- if (!i7core_dev)
- return -ENOMEM;
- i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs,
- GFP_KERNEL);
- if (!i7core_dev->pdev) {
- kfree(i7core_dev);
+ i7core_dev = alloc_i7core_dev(socket, table);
+ if (!i7core_dev) {
+ pci_dev_put(pdev);
return -ENOMEM;
}
- i7core_dev->socket = socket;
- i7core_dev->n_devs = n_devs;
- list_add_tail(&i7core_dev->list, &i7core_edac_list);
}
if (i7core_dev->pdev[devno]) {
@@ -1368,27 +1396,31 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno,
dev_descr->func,
PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+ /*
+ * As stated on drivers/pci/search.c, the reference count for
+ * @from is always decremented if it is not %NULL. So, as we need
+ * to get all devices up to null, we need to do a get for the device
+ */
+ pci_dev_get(pdev);
+
*prev = pdev;
return 0;
}
-static int i7core_get_devices(struct pci_id_table *table)
+static int i7core_get_all_devices(void)
{
int i, rc, last_bus;
struct pci_dev *pdev = NULL;
- struct pci_id_descr *dev_descr;
+ const struct pci_id_table *table = pci_dev_table;
last_bus = i7core_pci_lastbus();
while (table && table->descr) {
- dev_descr = table->descr;
for (i = 0; i < table->n_devs; i++) {
pdev = NULL;
do {
- rc = i7core_get_onedevice(&pdev, i,
- &dev_descr[i],
- table->n_devs,
+ rc = i7core_get_onedevice(&pdev, table, i,
last_bus);
if (rc < 0) {
if (i == 0) {
@@ -1404,7 +1436,6 @@ static int i7core_get_devices(struct pci_id_table *table)
}
return 0;
- return 0;
}
static int mci_bind_devs(struct mem_ctl_info *mci,
@@ -1414,10 +1445,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
struct pci_dev *pdev;
int i, func, slot;
- /* Associates i7core_dev and mci for future usage */
- pvt->i7core_dev = i7core_dev;
- i7core_dev->mci = mci;
-
pvt->is_registered = 0;
for (i = 0; i < i7core_dev->n_devs; i++) {
pdev = i7core_dev->pdev[i];
@@ -1448,15 +1475,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
pvt->is_registered = 1;
}
- /*
- * Add extra nodes to count errors on udimm
- * For registered memory, this is not needed, since the counters
- * are already displayed at the standard locations
- */
- if (!pvt->is_registered)
- i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
- &i7core_udimm_counters;
-
return 0;
error:
@@ -1470,7 +1488,9 @@ error:
Error check routines
****************************************************************************/
static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
- int chan, int dimm, int add)
+ const int chan,
+ const int dimm,
+ const int add)
{
char *msg;
struct i7core_pvt *pvt = mci->pvt_info;
@@ -1487,7 +1507,10 @@ static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
}
static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
- int chan, int new0, int new1, int new2)
+ const int chan,
+ const int new0,
+ const int new1,
+ const int new2)
{
struct i7core_pvt *pvt = mci->pvt_info;
int add0 = 0, add1 = 0, add2 = 0;
@@ -1641,7 +1664,7 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
* fields
*/
static void i7core_mce_output_error(struct mem_ctl_info *mci,
- struct mce *m)
+ const struct mce *m)
{
struct i7core_pvt *pvt = mci->pvt_info;
char *type, *optype, *err, *msg;
@@ -1845,28 +1868,85 @@ static int i7core_mce_check_error(void *priv, struct mce *mce)
return 1;
}
-static int i7core_register_mci(struct i7core_dev *i7core_dev,
- int num_channels, int num_csrows)
+static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
+{
+ pvt->i7core_pci = edac_pci_create_generic_ctl(
+ &pvt->i7core_dev->pdev[0]->dev,
+ EDAC_MOD_STR);
+ if (unlikely(!pvt->i7core_pci))
+ pr_warn("Unable to setup PCI error report via EDAC\n");
+}
+
+static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
+{
+ if (likely(pvt->i7core_pci))
+ edac_pci_release_generic_ctl(pvt->i7core_pci);
+ else
+ i7core_printk(KERN_ERR,
+ "Couldn't find mem_ctl_info for socket %d\n",
+ pvt->i7core_dev->socket);
+ pvt->i7core_pci = NULL;
+}
+
+static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
+{
+ struct mem_ctl_info *mci = i7core_dev->mci;
+ struct i7core_pvt *pvt;
+
+ if (unlikely(!mci || !mci->pvt_info)) {
+ debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
+ __func__, &i7core_dev->pdev[0]->dev);
+
+ i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
+ return;
+ }
+
+ pvt = mci->pvt_info;
+
+ debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
+ __func__, mci, &i7core_dev->pdev[0]->dev);
+
+ /* Disable MCE NMI handler */
+ edac_mce_unregister(&pvt->edac_mce);
+
+ /* Disable EDAC polling */
+ i7core_pci_ctl_release(pvt);
+
+ /* Remove MC sysfs nodes */
+ edac_mc_del_mc(mci->dev);
+
+ debugf1("%s: free mci struct\n", mci->ctl_name);
+ kfree(mci->ctl_name);
+ edac_mc_free(mci);
+ i7core_dev->mci = NULL;
+}
+
+static int i7core_register_mci(struct i7core_dev *i7core_dev)
{
struct mem_ctl_info *mci;
struct i7core_pvt *pvt;
- int csrow = 0;
- int rc;
+ int rc, channels, csrows;
+
+ /* Check the number of active and not disabled channels */
+ rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows);
+ if (unlikely(rc < 0))
+ return rc;
/* allocate a new MC control structure */
- mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
- i7core_dev->socket);
+ mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket);
if (unlikely(!mci))
return -ENOMEM;
- debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
-
- /* record ptr to the generic device */
- mci->dev = &i7core_dev->pdev[0]->dev;
+ debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
+ __func__, mci, &i7core_dev->pdev[0]->dev);
pvt = mci->pvt_info;
memset(pvt, 0, sizeof(*pvt));
+ /* Associates i7core_dev and mci for future usage */
+ pvt->i7core_dev = i7core_dev;
+ i7core_dev->mci = mci;
+
/*
* FIXME: how to handle RDDR3 at MCI level? It is possible to have
* Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
@@ -1881,17 +1961,23 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
i7core_dev->socket);
mci->dev_name = pci_name(i7core_dev->pdev[0]);
mci->ctl_page_to_phys = NULL;
- mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
- /* Set the function pointer to an actual operation function */
- mci->edac_check = i7core_check_error;
/* Store pci devices at mci for faster access */
rc = mci_bind_devs(mci, i7core_dev);
if (unlikely(rc < 0))
- goto fail;
+ goto fail0;
+
+ if (pvt->is_registered)
+ mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
+ else
+ mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
/* Get dimm basic config */
- get_dimm_config(mci, &csrow);
+ get_dimm_config(mci);
+ /* record ptr to the generic device */
+ mci->dev = &i7core_dev->pdev[0]->dev;
+ /* Set the function pointer to an actual operation function */
+ mci->edac_check = i7core_check_error;
/* add this new MC control structure to EDAC's list of MCs */
if (unlikely(edac_mc_add_mc(mci))) {
@@ -1902,19 +1988,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
*/
rc = -EINVAL;
- goto fail;
- }
-
- /* allocating generic PCI control info */
- i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
- EDAC_MOD_STR);
- if (unlikely(!i7core_pci)) {
- printk(KERN_WARNING
- "%s(): Unable to create PCI control\n",
- __func__);
- printk(KERN_WARNING
- "%s(): PCI error report via EDAC not setup\n",
- __func__);
+ goto fail0;
}
/* Default error mask is any memory */
@@ -1925,19 +1999,28 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev,
pvt->inject.page = -1;
pvt->inject.col = -1;
+ /* allocating generic PCI control info */
+ i7core_pci_ctl_create(pvt);
+
/* Registers on edac_mce in order to receive memory errors */
pvt->edac_mce.priv = mci;
pvt->edac_mce.check_error = i7core_mce_check_error;
-
rc = edac_mce_register(&pvt->edac_mce);
if (unlikely(rc < 0)) {
debugf0("MC: " __FILE__
": %s(): failed edac_mce_register()\n", __func__);
+ goto fail1;
}
-fail:
- if (rc < 0)
- edac_mc_free(mci);
+ return 0;
+
+fail1:
+ i7core_pci_ctl_release(pvt);
+ edac_mc_del_mc(mci->dev);
+fail0:
+ kfree(mci->ctl_name);
+ edac_mc_free(mci);
+ i7core_dev->mci = NULL;
return rc;
}
@@ -1949,8 +2032,6 @@ fail:
* < 0 for error code
*/
-static int probed = 0;
-
static int __devinit i7core_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -1965,25 +2046,16 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
*/
if (unlikely(probed >= 1)) {
mutex_unlock(&i7core_edac_lock);
- return -EINVAL;
+ return -ENODEV;
}
probed++;
- rc = i7core_get_devices(pci_dev_table);
+ rc = i7core_get_all_devices();
if (unlikely(rc < 0))
goto fail0;
list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
- int channels;
- int csrows;
-
- /* Check the number of active and not disabled channels */
- rc = i7core_get_active_channels(i7core_dev->socket,
- &channels, &csrows);
- if (unlikely(rc < 0))
- goto fail1;
-
- rc = i7core_register_mci(i7core_dev, channels, csrows);
+ rc = i7core_register_mci(i7core_dev);
if (unlikely(rc < 0))
goto fail1;
}
@@ -1994,6 +2066,9 @@ static int __devinit i7core_probe(struct pci_dev *pdev,
return 0;
fail1:
+ list_for_each_entry(i7core_dev, &i7core_edac_list, list)
+ i7core_unregister_mci(i7core_dev);
+
i7core_put_all_devices();
fail0:
mutex_unlock(&i7core_edac_lock);
@@ -2006,14 +2081,10 @@ fail0:
*/
static void __devexit i7core_remove(struct pci_dev *pdev)
{
- struct mem_ctl_info *mci;
- struct i7core_dev *i7core_dev, *tmp;
+ struct i7core_dev *i7core_dev;
debugf0(__FILE__ ": %s()\n", __func__);
- if (i7core_pci)
- edac_pci_release_generic_ctl(i7core_pci);
-
/*
* we have a trouble here: pdev value for removal will be wrong, since
* it will point to the X58 register used to detect that the machine
@@ -2023,22 +2094,18 @@ static void __devexit i7core_remove(struct pci_dev *pdev)
*/
mutex_lock(&i7core_edac_lock);
- list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
- mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
- if (mci) {
- struct i7core_pvt *pvt = mci->pvt_info;
-
- i7core_dev = pvt->i7core_dev;
- edac_mce_unregister(&pvt->edac_mce);
- kfree(mci->ctl_name);
- edac_mc_free(mci);
- i7core_put_devices(i7core_dev);
- } else {
- i7core_printk(KERN_ERR,
- "Couldn't find mci for socket %d\n",
- i7core_dev->socket);
- }
+
+ if (unlikely(!probed)) {
+ mutex_unlock(&i7core_edac_lock);
+ return;
}
+
+ list_for_each_entry(i7core_dev, &i7core_edac_list, list)
+ i7core_unregister_mci(i7core_dev);
+
+ /* Release PCI resources */
+ i7core_put_all_devices();
+
probed--;
mutex_unlock(&i7core_edac_lock);
@@ -2070,7 +2137,8 @@ static int __init i7core_init(void)
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
- i7core_xeon_pci_fixup(pci_dev_table);
+ if (use_pci_fixup)
+ i7core_xeon_pci_fixup(pci_dev_table);
pci_rc = pci_register_driver(&i7core_driver);
diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig
index fcf3ea28340b..40a222e19b2d 100644
--- a/drivers/firewire/Kconfig
+++ b/drivers/firewire/Kconfig
@@ -3,9 +3,6 @@ menu "IEEE 1394 (FireWire) support"
# firewire-core does not depend on PCI but is
# not useful without PCI controller driver
-comment "You can enable one or both FireWire driver stacks."
-comment "The newer stack is recommended."
-
config FIREWIRE
tristate "FireWire driver stack"
select CRC_ITU_T
@@ -64,8 +61,6 @@ config FIREWIRE_NET
To compile this driver as a module, say M here: The module will be
called firewire-net.
-source "drivers/ieee1394/Kconfig"
-
config FIREWIRE_NOSY
tristate "Nosy - a FireWire traffic sniffer for PCILynx cards"
depends on PCI
diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile
index 3c6a7fb20aa7..e3870d5c43dd 100644
--- a/drivers/firewire/Makefile
+++ b/drivers/firewire/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_FIREWIRE_OHCI) += firewire-ohci.o
obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o
obj-$(CONFIG_FIREWIRE_NET) += firewire-net.o
obj-$(CONFIG_FIREWIRE_NOSY) += nosy.o
+obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o
diff --git a/drivers/ieee1394/init_ohci1394_dma.c b/drivers/firewire/init_ohci1394_dma.c
index ddaab6eb8ace..a9a347adb353 100644
--- a/drivers/ieee1394/init_ohci1394_dma.c
+++ b/drivers/firewire/init_ohci1394_dma.c
@@ -32,23 +32,41 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <linux/interrupt.h> /* for ohci1394.h */
#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/pci.h> /* for PCI defines */
-#include <linux/init_ohci1394_dma.h>
+#include <linux/string.h>
+
#include <asm/pci-direct.h> /* for direct PCI config space access */
#include <asm/fixmap.h>
-#include "ieee1394_types.h"
-#include "ohci1394.h"
+#include <linux/init_ohci1394_dma.h>
+#include "ohci.h"
int __initdata init_ohci1394_dma_early;
+struct ohci {
+ void __iomem *registers;
+};
+
+static inline void reg_write(const struct ohci *ohci, int offset, u32 data)
+{
+ writel(data, ohci->registers + offset);
+}
+
+static inline u32 reg_read(const struct ohci *ohci, int offset)
+{
+ return readl(ohci->registers + offset);
+}
+
+#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */
+
/* Reads a PHY register of an OHCI-1394 controller */
-static inline u8 __init get_phy_reg(struct ti_ohci *ohci, u8 addr)
+static inline u8 __init get_phy_reg(struct ohci *ohci, u8 addr)
{
int i;
- quadlet_t r;
+ u32 r;
reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000);
@@ -63,22 +81,22 @@ static inline u8 __init get_phy_reg(struct ti_ohci *ohci, u8 addr)
}
/* Writes to a PHY register of an OHCI-1394 controller */
-static inline void __init set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data)
+static inline void __init set_phy_reg(struct ohci *ohci, u8 addr, u8 data)
{
int i;
reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
- u32 r = reg_read(ohci, OHCI1394_PhyControl);
- if (!(r & 0x00004000))
+ if (!(reg_read(ohci, OHCI1394_PhyControl) & 0x00004000))
break;
mdelay(1);
}
}
/* Resets an OHCI-1394 controller (for sane state before initialization) */
-static inline void __init init_ohci1394_soft_reset(struct ti_ohci *ohci) {
+static inline void __init init_ohci1394_soft_reset(struct ohci *ohci)
+{
int i;
reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
@@ -91,10 +109,14 @@ static inline void __init init_ohci1394_soft_reset(struct ti_ohci *ohci) {
}
}
+#define OHCI1394_MAX_AT_REQ_RETRIES 0xf
+#define OHCI1394_MAX_AT_RESP_RETRIES 0x2
+#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
+
/* Basic OHCI-1394 register and port inititalization */
-static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci)
+static inline void __init init_ohci1394_initialize(struct ohci *ohci)
{
- quadlet_t bus_options;
+ u32 bus_options;
int num_ports, i;
/* Put some defaults to these undefined bus options */
@@ -116,7 +138,7 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci)
/* enable phys */
reg_write(ohci, OHCI1394_LinkControlSet,
- OHCI1394_LinkControl_RcvPhyPkt);
+ OHCI1394_LinkControl_rcvPhyPkt);
/* Don't accept phy packets into AR request context */
reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
@@ -128,7 +150,7 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci)
reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
/* Accept asyncronous transfer requests from all nodes for now */
- reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000);
+ reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
/* Specify asyncronous transfer retries */
reg_write(ohci, OHCI1394_ATRetries,
@@ -137,7 +159,8 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci)
(OHCI1394_MAX_PHYS_RESP_RETRIES<<8));
/* We don't want hardware swapping */
- reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap);
+ reg_write(ohci, OHCI1394_HCControlClear,
+ OHCI1394_HCControl_noByteSwapData);
/* Enable link */
reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
@@ -164,11 +187,11 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci)
* has to be enabled after each bus reset when needed. We resort
* to polling here because on early boot, we have no interrupts.
*/
-static inline void __init init_ohci1394_wait_for_busresets(struct ti_ohci *ohci)
+static inline void __init init_ohci1394_wait_for_busresets(struct ohci *ohci)
{
int i, events;
- for (i=0; i < 9; i++) {
+ for (i = 0; i < 9; i++) {
mdelay(200);
events = reg_read(ohci, OHCI1394_IntEventSet);
if (events & OHCI1394_busReset)
@@ -182,18 +205,18 @@ static inline void __init init_ohci1394_wait_for_busresets(struct ti_ohci *ohci)
* This enables remote DMA access over IEEE1394 from every host for the low
* 4GB of address space. DMA accesses above 4GB are not available currently.
*/
-static inline void __init init_ohci1394_enable_physical_dma(struct ti_ohci *hci)
+static inline void __init init_ohci1394_enable_physical_dma(struct ohci *ohci)
{
- reg_write(hci, OHCI1394_PhyReqFilterHiSet, 0xffffffff);
- reg_write(hci, OHCI1394_PhyReqFilterLoSet, 0xffffffff);
- reg_write(hci, OHCI1394_PhyUpperBound, 0xffff0000);
+ reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 0xffffffff);
+ reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 0xffffffff);
+ reg_write(ohci, OHCI1394_PhyUpperBound, 0xffff0000);
}
/**
* init_ohci1394_reset_and_init_dma - init controller and enable DMA
* This initializes the given controller and enables physical DMA engine in it.
*/
-static inline void __init init_ohci1394_reset_and_init_dma(struct ti_ohci *ohci)
+static inline void __init init_ohci1394_reset_and_init_dma(struct ohci *ohci)
{
/* Start off with a soft reset, clears everything to a sane state. */
init_ohci1394_soft_reset(ohci);
@@ -225,7 +248,7 @@ static inline void __init init_ohci1394_reset_and_init_dma(struct ti_ohci *ohci)
static inline void __init init_ohci1394_controller(int num, int slot, int func)
{
unsigned long ohci_base;
- struct ti_ohci ohci;
+ struct ohci ohci;
printk(KERN_INFO "init_ohci1394_dma: initializing OHCI-1394"
" at %02x:%02x.%x\n", num, slot, func);
@@ -235,7 +258,7 @@ static inline void __init init_ohci1394_controller(int num, int slot, int func)
set_fixmap_nocache(FIX_OHCI1394_BASE, ohci_base);
- ohci.registers = (void *)fix_to_virt(FIX_OHCI1394_BASE);
+ ohci.registers = (void __iomem *)fix_to_virt(FIX_OHCI1394_BASE);
init_ohci1394_reset_and_init_dma(&ohci);
}
@@ -247,6 +270,7 @@ static inline void __init init_ohci1394_controller(int num, int slot, int func)
void __init init_ohci1394_dma_on_all_controllers(void)
{
int num, slot, func;
+ u32 class;
if (!early_pci_allowed())
return;
@@ -255,9 +279,9 @@ void __init init_ohci1394_dma_on_all_controllers(void)
for (num = 0; num < 32; num++) {
for (slot = 0; slot < 32; slot++) {
for (func = 0; func < 8; func++) {
- u32 class = read_pci_config(num,slot,func,
+ class = read_pci_config(num, slot, func,
PCI_CLASS_REVISION);
- if ((class == 0xffffffff))
+ if (class == 0xffffffff)
continue; /* No device at this func */
if (class>>8 != PCI_CLASS_SERIAL_FIREWIRE_OHCI)
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index a2b12aa1f2b9..501866662e05 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -345,7 +345,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid)
do {
level = __ffs(pending);
- handle_nested_irq(level + chip->irq_base);
+ generic_handle_irq(level + chip->irq_base);
pending &= ~(1 << level);
} while (pending);
@@ -360,7 +360,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
struct pca953x_platform_data *pdata = client->dev.platform_data;
int ret;
- if (pdata->irq_base && (id->driver_data & PCA953X_INT)) {
+ if (pdata->irq_base != -1
+ && (id->driver_data & PCA953X_INT)) {
int lvl;
ret = pca953x_read_reg(chip, PCA953X_INPUT,
@@ -383,7 +384,6 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
set_irq_chip_data(irq, chip);
set_irq_chip_and_handler(irq, &pca953x_irq_chip,
handle_edge_irq);
- set_irq_nested_thread(irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
#else
@@ -394,6 +394,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
ret = request_threaded_irq(client->irq,
NULL,
pca953x_irq_handler,
+ IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(&client->dev), chip);
if (ret) {
@@ -408,13 +409,13 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
return 0;
out_failed:
- chip->irq_base = 0;
+ chip->irq_base = -1;
return ret;
}
static void pca953x_irq_teardown(struct pca953x_chip *chip)
{
- if (chip->irq_base)
+ if (chip->irq_base != -1)
free_irq(chip->client->irq, chip);
}
#else /* CONFIG_GPIO_PCA953X_IRQ */
@@ -424,7 +425,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
struct i2c_client *client = chip->client;
struct pca953x_platform_data *pdata = client->dev.platform_data;
- if (pdata->irq_base && (id->driver_data & PCA953X_INT))
+ if (pdata->irq_base != -1 && (id->driver_data & PCA953X_INT))
dev_warn(&client->dev, "interrupt support not compiled in\n");
return 0;
diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c
index 709690995d0d..846fbd5e31bf 100644
--- a/drivers/gpio/xilinx_gpio.c
+++ b/drivers/gpio/xilinx_gpio.c
@@ -171,13 +171,13 @@ static int __devinit xgpio_of_probe(struct device_node *np)
/* Update GPIO state shadow register with default value */
tree_info = of_get_property(np, "xlnx,dout-default", NULL);
if (tree_info)
- chip->gpio_state = *tree_info;
+ chip->gpio_state = be32_to_cpup(tree_info);
/* Update GPIO direction shadow register with default value */
chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */
tree_info = of_get_property(np, "xlnx,tri-default", NULL);
if (tree_info)
- chip->gpio_dir = *tree_info;
+ chip->gpio_dir = be32_to_cpup(tree_info);
/* Check device node and parent device node for device width */
chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */
@@ -186,7 +186,7 @@ static int __devinit xgpio_of_probe(struct device_node *np)
tree_info = of_get_property(np->parent,
"xlnx,gpio-width", NULL);
if (tree_info)
- chip->mmchip.gc.ngpio = *tree_info;
+ chip->mmchip.gc.ngpio = be32_to_cpup(tree_info);
spin_lock_init(&chip->gpio_lock);
diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
index 30879df3daea..cc9277885dd0 100644
--- a/drivers/gpu/Makefile
+++ b/drivers/gpu/Makefile
@@ -1 +1 @@
-obj-y += drm/ vga/
+obj-y += drm/ vga/ stub/
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f3a23a329f4e..997c43d04909 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -5,7 +5,7 @@
ccflags-y := -Iinclude/drm
drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
- drm_context.o drm_dma.o drm_drawable.o \
+ drm_context.o drm_dma.o \
drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \
drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c
index ba38e0147220..252fdb98b73a 100644
--- a/drivers/gpu/drm/drm_agpsupport.c
+++ b/drivers/gpu/drm/drm_agpsupport.c
@@ -193,7 +193,7 @@ int drm_agp_enable_ioctl(struct drm_device *dev, void *data,
* \return zero on success or a negative number on failure.
*
* Verifies the AGP device is present and has been acquired, allocates the
- * memory via alloc_agp() and creates a drm_agp_mem entry for it.
+ * memory via agp_allocate_memory() and creates a drm_agp_mem entry for it.
*/
int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
{
@@ -211,7 +211,7 @@ int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
type = (u32) request->type;
- if (!(memory = drm_alloc_agp(dev, pages, type))) {
+ if (!(memory = agp_allocate_memory(dev->agp->bridge, pages, type))) {
kfree(entry);
return -ENOMEM;
}
@@ -423,38 +423,6 @@ struct drm_agp_head *drm_agp_init(struct drm_device *dev)
return head;
}
-/** Calls agp_allocate_memory() */
-DRM_AGP_MEM *drm_agp_allocate_memory(struct agp_bridge_data * bridge,
- size_t pages, u32 type)
-{
- return agp_allocate_memory(bridge, pages, type);
-}
-
-/** Calls agp_free_memory() */
-int drm_agp_free_memory(DRM_AGP_MEM * handle)
-{
- if (!handle)
- return 0;
- agp_free_memory(handle);
- return 1;
-}
-
-/** Calls agp_bind_memory() */
-int drm_agp_bind_memory(DRM_AGP_MEM * handle, off_t start)
-{
- if (!handle)
- return -EINVAL;
- return agp_bind_memory(handle, start);
-}
-
-/** Calls agp_unbind_memory() */
-int drm_agp_unbind_memory(DRM_AGP_MEM * handle)
-{
- if (!handle)
- return -EINVAL;
- return agp_unbind_memory(handle);
-}
-
/**
* Binds a collection of pages into AGP memory at the given offset, returning
* the AGP memory structure containing them.
@@ -474,7 +442,7 @@ drm_agp_bind_pages(struct drm_device *dev,
DRM_DEBUG("\n");
- mem = drm_agp_allocate_memory(dev->agp->bridge, num_pages,
+ mem = agp_allocate_memory(dev->agp->bridge, num_pages,
type);
if (mem == NULL) {
DRM_ERROR("Failed to allocate memory for %ld pages\n",
@@ -487,7 +455,7 @@ drm_agp_bind_pages(struct drm_device *dev,
mem->page_count = num_pages;
mem->is_flushed = true;
- ret = drm_agp_bind_memory(mem, gtt_offset / PAGE_SIZE);
+ ret = agp_bind_memory(mem, gtt_offset / PAGE_SIZE);
if (ret != 0) {
DRM_ERROR("Failed to bind AGP memory: %d\n", ret);
agp_free_memory(mem);
diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c
index 2607753a320b..6d440fb894cf 100644
--- a/drivers/gpu/drm/drm_context.c
+++ b/drivers/gpu/drm/drm_context.c
@@ -333,14 +333,6 @@ int drm_addctx(struct drm_device *dev, void *data,
return -ENOMEM;
}
- if (ctx->handle != DRM_KERNEL_CONTEXT) {
- if (dev->driver->context_ctor)
- if (!dev->driver->context_ctor(dev, ctx->handle)) {
- DRM_DEBUG("Running out of ctxs or memory.\n");
- return -ENOMEM;
- }
- }
-
ctx_entry = kmalloc(sizeof(*ctx_entry), GFP_KERNEL);
if (!ctx_entry) {
DRM_DEBUG("out of memory\n");
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 37e0b4fa482a..6985cb1da72c 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1854,7 +1854,8 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
}
if (fb->funcs->dirty) {
- ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips);
+ ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
+ clips, num_clips);
} else {
ret = -ENOSYS;
goto out_err2;
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 677b275fa721..9d8c892d07c9 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -48,7 +48,6 @@ static struct drm_info_list drm_debugfs_list[] = {
{"queues", drm_queues_info, 0},
{"bufs", drm_bufs_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM},
- {"gem_objects", drm_gem_object_info, DRIVER_GEM},
#if DRM_DEBUG_CODE
{"vma", drm_vma_info, 0},
#endif
diff --git a/drivers/gpu/drm/drm_drawable.c b/drivers/gpu/drm/drm_drawable.c
deleted file mode 100644
index c53c9768cc11..000000000000
--- a/drivers/gpu/drm/drm_drawable.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/**
- * \file drm_drawable.c
- * IOCTLs for drawables
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- * \author Michel Dänzer <michel@tungstengraphics.com>
- */
-
-/*
- * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com
- *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
- * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
- * Copyright 2006 Tungsten Graphics, Inc., Bismarck, North Dakota.
- * 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, 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.
- */
-
-#include "drmP.h"
-
-/**
- * Allocate drawable ID and memory to store information about it.
- */
-int drm_adddraw(struct drm_device *dev, void *data, struct drm_file *file_priv)
-{
- unsigned long irqflags;
- struct drm_draw *draw = data;
- int new_id = 0;
- int ret;
-
-again:
- if (idr_pre_get(&dev->drw_idr, GFP_KERNEL) == 0) {
- DRM_ERROR("Out of memory expanding drawable idr\n");
- return -ENOMEM;
- }
-
- spin_lock_irqsave(&dev->drw_lock, irqflags);
- ret = idr_get_new_above(&dev->drw_idr, NULL, 1, &new_id);
- if (ret == -EAGAIN) {
- spin_unlock_irqrestore(&dev->drw_lock, irqflags);
- goto again;
- }
-
- spin_unlock_irqrestore(&dev->drw_lock, irqflags);
-
- draw->handle = new_id;
-
- DRM_DEBUG("%d\n", draw->handle);
-
- return 0;
-}
-
-/**
- * Free drawable ID and memory to store information about it.
- */
-int drm_rmdraw(struct drm_device *dev, void *data, struct drm_file *file_priv)
-{
- struct drm_draw *draw = data;
- unsigned long irqflags;
- struct drm_drawable_info *info;
-
- spin_lock_irqsave(&dev->drw_lock, irqflags);
-
- info = drm_get_drawable_info(dev, draw->handle);
- if (info == NULL) {
- spin_unlock_irqrestore(&dev->drw_lock, irqflags);
- return -EINVAL;
- }
- kfree(info->rects);
- kfree(info);
-
- idr_remove(&dev->drw_idr, draw->handle);
-
- spin_unlock_irqrestore(&dev->drw_lock, irqflags);
- DRM_DEBUG("%d\n", draw->handle);
- return 0;
-}
-
-int drm_update_drawable_info(struct drm_device *dev, void *data, struct drm_file *file_priv)
-{
- struct drm_update_draw *update = data;
- unsigned long irqflags;
- struct drm_clip_rect *rects;
- struct drm_drawable_info *info;
- int err;
-
- info = idr_find(&dev->drw_idr, update->handle);
- if (!info) {
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
- if (IS_ERR(idr_replace(&dev->drw_idr, info, update->handle))) {
- DRM_ERROR("No such drawable %d\n", update->handle);
- kfree(info);
- return -EINVAL;
- }
- }
-
- switch (update->type) {
- case DRM_DRAWABLE_CLIPRECTS:
- if (update->num == 0)
- rects = NULL;
- else if (update->num != info->num_rects) {
- rects = kmalloc(update->num *
- sizeof(struct drm_clip_rect),
- GFP_KERNEL);
- } else
- rects = info->rects;
-
- if (update->num && !rects) {
- DRM_ERROR("Failed to allocate cliprect memory\n");
- err = -ENOMEM;
- goto error;
- }
-
- if (update->num && DRM_COPY_FROM_USER(rects,
- (struct drm_clip_rect __user *)
- (unsigned long)update->data,
- update->num *
- sizeof(*rects))) {
- DRM_ERROR("Failed to copy cliprects from userspace\n");
- err = -EFAULT;
- goto error;
- }
-
- spin_lock_irqsave(&dev->drw_lock, irqflags);
-
- if (rects != info->rects) {
- kfree(info->rects);
- }
-
- info->rects = rects;
- info->num_rects = update->num;
-
- spin_unlock_irqrestore(&dev->drw_lock, irqflags);
-
- DRM_DEBUG("Updated %d cliprects for drawable %d\n",
- info->num_rects, update->handle);
- break;
- default:
- DRM_ERROR("Invalid update type %d\n", update->type);
- return -EINVAL;
- }
-
- return 0;
-
-error:
- if (rects != info->rects)
- kfree(rects);
-
- return err;
-}
-
-/**
- * Caller must hold the drawable spinlock!
- */
-struct drm_drawable_info *drm_get_drawable_info(struct drm_device *dev, drm_drawable_t id)
-{
- return idr_find(&dev->drw_idr, id);
-}
-EXPORT_SYMBOL(drm_get_drawable_info);
-
-static int drm_drawable_free(int idr, void *p, void *data)
-{
- struct drm_drawable_info *info = p;
-
- if (info) {
- kfree(info->rects);
- kfree(info);
- }
-
- return 0;
-}
-
-void drm_drawable_free_all(struct drm_device *dev)
-{
- idr_for_each(&dev->drw_idr, drm_drawable_free, NULL);
- idr_remove_all(&dev->drw_idr);
-}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index ff6690f4fc87..271835a71570 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -91,8 +91,8 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_resctx, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_adddraw, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_rmdraw, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_lock, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_unlock, DRM_AUTH),
@@ -127,7 +127,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
- DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
@@ -180,10 +180,6 @@ int drm_lastclose(struct drm_device * dev)
mutex_lock(&dev->struct_mutex);
- /* Free drawable information memory */
- drm_drawable_free_all(dev);
- del_timer(&dev->timer);
-
/* Clear AGP information */
if (drm_core_has_AGP(dev) && dev->agp &&
!drm_core_check_feature(dev, DRIVER_MODESET)) {
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 96e963108225..c1a26217a530 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -30,7 +30,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include "drmP.h"
#include "drm_edid.h"
#include "drm_edid_modes.h"
@@ -1268,34 +1267,51 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
}
#define HDMI_IDENTIFIER 0x000C03
+#define AUDIO_BLOCK 0x01
#define VENDOR_BLOCK 0x03
+#define EDID_BASIC_AUDIO (1 << 6)
+
/**
- * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
- * @edid: monitor EDID information
- *
- * Parse the CEA extension according to CEA-861-B.
- * Return true if HDMI, false if not or unknown.
+ * Search EDID for CEA extension block.
*/
-bool drm_detect_hdmi_monitor(struct edid *edid)
+static u8 *drm_find_cea_extension(struct edid *edid)
{
- char *edid_ext = NULL;
- int i, hdmi_id;
- int start_offset, end_offset;
- bool is_hdmi = false;
+ u8 *edid_ext = NULL;
+ int i;
/* No EDID or EDID extensions */
if (edid == NULL || edid->extensions == 0)
- goto end;
+ return NULL;
/* Find CEA extension */
for (i = 0; i < edid->extensions; i++) {
- edid_ext = (char *)edid + EDID_LENGTH * (i + 1);
- /* This block is CEA extension */
- if (edid_ext[0] == 0x02)
+ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+ if (edid_ext[0] == CEA_EXT)
break;
}
if (i == edid->extensions)
+ return NULL;
+
+ return edid_ext;
+}
+
+/**
+ * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
+ * @edid: monitor EDID information
+ *
+ * Parse the CEA extension according to CEA-861-B.
+ * Return true if HDMI, false if not or unknown.
+ */
+bool drm_detect_hdmi_monitor(struct edid *edid)
+{
+ u8 *edid_ext;
+ int i, hdmi_id;
+ int start_offset, end_offset;
+ bool is_hdmi = false;
+
+ edid_ext = drm_find_cea_extension(edid);
+ if (!edid_ext)
goto end;
/* Data block offset in CEA extension block */
@@ -1326,6 +1342,53 @@ end:
EXPORT_SYMBOL(drm_detect_hdmi_monitor);
/**
+ * drm_detect_monitor_audio - check monitor audio capability
+ *
+ * Monitor should have CEA extension block.
+ * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
+ * audio' only. If there is any audio extension block and supported
+ * audio format, assume at least 'basic audio' support, even if 'basic
+ * audio' is not defined in EDID.
+ *
+ */
+bool drm_detect_monitor_audio(struct edid *edid)
+{
+ u8 *edid_ext;
+ int i, j;
+ bool has_audio = false;
+ int start_offset, end_offset;
+
+ edid_ext = drm_find_cea_extension(edid);
+ if (!edid_ext)
+ goto end;
+
+ has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
+
+ if (has_audio) {
+ DRM_DEBUG_KMS("Monitor has basic audio support\n");
+ goto end;
+ }
+
+ /* Data block offset in CEA extension block */
+ start_offset = 4;
+ end_offset = edid_ext[2];
+
+ for (i = start_offset; i < end_offset;
+ i += ((edid_ext[i] & 0x1f) + 1)) {
+ if ((edid_ext[i] >> 5) == AUDIO_BLOCK) {
+ has_audio = true;
+ for (j = 1; j < (edid_ext[i] & 0x1f); j += 3)
+ DRM_DEBUG_KMS("CEA audio format %d\n",
+ (edid_ext[i + j] >> 3) & 0xf);
+ goto end;
+ }
+ }
+end:
+ return has_audio;
+}
+EXPORT_SYMBOL(drm_detect_monitor_audio);
+
+/**
* drm_add_edid_modes - add modes from EDID data, if available
* @connector: connector we're probing
* @edid: edid data
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 6a5e403f9aa1..d2849e4ea4d0 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -242,6 +242,30 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
return 0;
}
+static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
+{
+ uint16_t *r_base, *g_base, *b_base;
+ int i;
+
+ r_base = crtc->gamma_store;
+ g_base = r_base + crtc->gamma_size;
+ b_base = g_base + crtc->gamma_size;
+
+ for (i = 0; i < crtc->gamma_size; i++)
+ helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
+}
+
+static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
+{
+ uint16_t *r_base, *g_base, *b_base;
+
+ r_base = crtc->gamma_store;
+ g_base = r_base + crtc->gamma_size;
+ b_base = g_base + crtc->gamma_size;
+
+ crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
+}
+
int drm_fb_helper_debug_enter(struct fb_info *info)
{
struct drm_fb_helper *helper = info->par;
@@ -260,11 +284,12 @@ int drm_fb_helper_debug_enter(struct fb_info *info)
continue;
funcs = mode_set->crtc->helper_private;
+ drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
funcs->mode_set_base_atomic(mode_set->crtc,
mode_set->fb,
mode_set->x,
- mode_set->y);
-
+ mode_set->y,
+ ENTER_ATOMIC_MODE_SET);
}
}
@@ -308,8 +333,9 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
continue;
}
+ drm_fb_helper_restore_lut_atomic(mode_set->crtc);
funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
- crtc->y);
+ crtc->y, LEAVE_ATOMIC_MODE_SET);
}
return 0;
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 5663d2719063..ea1c4b019ebf 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -92,12 +92,6 @@ drm_gem_init(struct drm_device *dev)
spin_lock_init(&dev->object_name_lock);
idr_init(&dev->object_name_idr);
- atomic_set(&dev->object_count, 0);
- atomic_set(&dev->object_memory, 0);
- atomic_set(&dev->pin_count, 0);
- atomic_set(&dev->pin_memory, 0);
- atomic_set(&dev->gtt_count, 0);
- atomic_set(&dev->gtt_memory, 0);
mm = kzalloc(sizeof(struct drm_gem_mm), GFP_KERNEL);
if (!mm) {
@@ -151,9 +145,6 @@ int drm_gem_object_init(struct drm_device *dev,
atomic_set(&obj->handle_count, 0);
obj->size = size;
- atomic_inc(&dev->object_count);
- atomic_add(obj->size, &dev->object_memory);
-
return 0;
}
EXPORT_SYMBOL(drm_gem_object_init);
@@ -180,8 +171,6 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size)
return obj;
fput:
/* Object_init mangles the global counters - readjust them. */
- atomic_dec(&dev->object_count);
- atomic_sub(obj->size, &dev->object_memory);
fput(obj->filp);
free:
kfree(obj);
@@ -436,10 +425,7 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
void
drm_gem_object_release(struct drm_gem_object *obj)
{
- struct drm_device *dev = obj->dev;
fput(obj->filp);
- atomic_dec(&dev->object_count);
- atomic_sub(obj->size, &dev->object_memory);
}
EXPORT_SYMBOL(drm_gem_object_release);
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index 974e970ce3f8..3cdbaf379bb5 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -270,20 +270,6 @@ int drm_gem_name_info(struct seq_file *m, void *data)
return 0;
}
-int drm_gem_object_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;
-
- seq_printf(m, "%d objects\n", atomic_read(&dev->object_count));
- seq_printf(m, "%d object bytes\n", atomic_read(&dev->object_memory));
- seq_printf(m, "%d pinned\n", atomic_read(&dev->pin_count));
- seq_printf(m, "%d pin bytes\n", atomic_read(&dev->pin_memory));
- seq_printf(m, "%d gtt bytes\n", atomic_read(&dev->gtt_memory));
- seq_printf(m, "%d gtt total\n", dev->gtt_total);
- return 0;
-}
-
#if DRM_DEBUG_CODE
int drm_vma_info(struct seq_file *m, void *data)
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index 9bf93bc9a32c..632ae243ede0 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -37,6 +37,8 @@
static int drm_notifier(void *priv);
+static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context);
+
/**
* Lock ioctl.
*
@@ -124,9 +126,6 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
}
- if (dev->driver->dma_ready && (lock->flags & _DRM_LOCK_READY))
- dev->driver->dma_ready(dev);
-
if (dev->driver->dma_quiescent && (lock->flags & _DRM_LOCK_QUIESCENT))
{
if (dev->driver->dma_quiescent(dev)) {
@@ -136,12 +135,6 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
}
}
- if (dev->driver->kernel_context_switch &&
- dev->last_context != lock->context) {
- dev->driver->kernel_context_switch(dev, dev->last_context,
- lock->context);
- }
-
return 0;
}
@@ -169,15 +162,8 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
- /* kernel_context_switch isn't used by any of the x86 drm
- * modules but is required by the Sparc driver.
- */
- if (dev->driver->kernel_context_switch_unlock)
- dev->driver->kernel_context_switch_unlock(dev);
- else {
- if (drm_lock_free(&master->lock, lock->context)) {
- /* FIXME: Should really bail out here. */
- }
+ if (drm_lock_free(&master->lock, lock->context)) {
+ /* FIXME: Should really bail out here. */
}
unblock_all_signals();
@@ -193,6 +179,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
*
* Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
*/
+static
int drm_lock_take(struct drm_lock_data *lock_data,
unsigned int context)
{
@@ -229,7 +216,6 @@ int drm_lock_take(struct drm_lock_data *lock_data,
}
return 0;
}
-EXPORT_SYMBOL(drm_lock_take);
/**
* This takes a lock forcibly and hands it to context. Should ONLY be used
@@ -297,7 +283,6 @@ int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context)
wake_up_interruptible(&lock_data->lock_queue);
return 0;
}
-EXPORT_SYMBOL(drm_lock_free);
/**
* If we get here, it means that the process has called DRM_IOCTL_LOCK
@@ -360,7 +345,6 @@ void drm_idlelock_take(struct drm_lock_data *lock_data)
}
spin_unlock_bh(&lock_data->spinlock);
}
-EXPORT_SYMBOL(drm_idlelock_take);
void drm_idlelock_release(struct drm_lock_data *lock_data)
{
@@ -380,8 +364,6 @@ void drm_idlelock_release(struct drm_lock_data *lock_data)
}
spin_unlock_bh(&lock_data->spinlock);
}
-EXPORT_SYMBOL(drm_idlelock_release);
-
int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
{
@@ -390,5 +372,3 @@ int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
_DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
master->lock.file_priv == file_priv);
}
-
-EXPORT_SYMBOL(drm_i_have_hw_lock);
diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c
index 7732268eced2..c9b805000a11 100644
--- a/drivers/gpu/drm/drm_memory.c
+++ b/drivers/gpu/drm/drm_memory.c
@@ -99,29 +99,23 @@ static void *agp_remap(unsigned long offset, unsigned long size,
return addr;
}
-/** Wrapper around agp_allocate_memory() */
-DRM_AGP_MEM *drm_alloc_agp(struct drm_device * dev, int pages, u32 type)
-{
- return drm_agp_allocate_memory(dev->agp->bridge, pages, type);
-}
-
/** Wrapper around agp_free_memory() */
-int drm_free_agp(DRM_AGP_MEM * handle, int pages)
+void drm_free_agp(DRM_AGP_MEM * handle, int pages)
{
- return drm_agp_free_memory(handle) ? 0 : -EINVAL;
+ agp_free_memory(handle);
}
EXPORT_SYMBOL(drm_free_agp);
/** Wrapper around agp_bind_memory() */
int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start)
{
- return drm_agp_bind_memory(handle, start);
+ return agp_bind_memory(handle, start);
}
/** Wrapper around agp_unbind_memory() */
int drm_unbind_agp(DRM_AGP_MEM * handle)
{
- return drm_agp_unbind_memory(handle);
+ return agp_unbind_memory(handle);
}
EXPORT_SYMBOL(drm_unbind_agp);
diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c
index a9ba6b69ad35..9e5b07efebb7 100644
--- a/drivers/gpu/drm/drm_proc.c
+++ b/drivers/gpu/drm/drm_proc.c
@@ -55,7 +55,6 @@ static struct drm_info_list drm_proc_list[] = {
{"queues", drm_queues_info, 0},
{"bufs", drm_bufs_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM},
- {"gem_objects", drm_gem_object_info, DRIVER_GEM},
#if DRM_DEBUG_CODE
{"vma", drm_vma_info, 0},
#endif
@@ -151,7 +150,6 @@ fail:
int drm_proc_init(struct drm_minor *minor, int minor_id,
struct proc_dir_entry *root)
{
- struct drm_device *dev = minor->dev;
char name[64];
int ret;
@@ -172,14 +170,6 @@ int drm_proc_init(struct drm_minor *minor, int minor_id,
return ret;
}
- if (dev->driver->proc_init) {
- ret = dev->driver->proc_init(minor);
- if (ret) {
- DRM_ERROR("DRM: Driver failed to initialize "
- "/proc/dri.\n");
- return ret;
- }
- }
return 0;
}
@@ -216,15 +206,11 @@ int drm_proc_remove_files(struct drm_info_list *files, int count,
*/
int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root)
{
- struct drm_device *dev = minor->dev;
char name[64];
if (!root || !minor->proc_root)
return 0;
- if (dev->driver->proc_cleanup)
- dev->driver->proc_cleanup(minor);
-
drm_proc_remove_files(drm_proc_list, DRM_PROC_ENTRIES, minor);
sprintf(name, "%d", minor->index);
diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c
index 9034c4c6100d..d15e09b0ae0b 100644
--- a/drivers/gpu/drm/drm_scatter.c
+++ b/drivers/gpu/drm/drm_scatter.c
@@ -184,8 +184,6 @@ int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request)
drm_sg_cleanup(entry);
return -ENOMEM;
}
-EXPORT_SYMBOL(drm_sg_alloc);
-
int drm_sg_alloc_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index d1ad57450df1..cdc89ee042cc 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -240,14 +240,10 @@ int drm_fill_in_dev(struct drm_device *dev,
INIT_LIST_HEAD(&dev->vblank_event_list);
spin_lock_init(&dev->count_lock);
- spin_lock_init(&dev->drw_lock);
spin_lock_init(&dev->event_lock);
- init_timer(&dev->timer);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
- idr_init(&dev->drw_idr);
-
if (drm_ht_create(&dev->map_hash, 12)) {
return -ENOMEM;
}
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index 5df450683aab..2c3fcbdfd8ff 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -523,14 +523,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
return 0;
}
-resource_size_t drm_core_get_map_ofs(struct drm_local_map * map)
-{
- return map->offset;
-}
-
-EXPORT_SYMBOL(drm_core_get_map_ofs);
-
-resource_size_t drm_core_get_reg_ofs(struct drm_device *dev)
+static resource_size_t drm_core_get_reg_ofs(struct drm_device *dev)
{
#ifdef __alpha__
return dev->hose->dense_mem_base - dev->hose->mem_space->start;
@@ -539,8 +532,6 @@ resource_size_t drm_core_get_reg_ofs(struct drm_device *dev)
#endif
}
-EXPORT_SYMBOL(drm_core_get_reg_ofs);
-
/**
* mmap DMA memory.
*
@@ -627,7 +618,7 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
#endif
case _DRM_FRAME_BUFFER:
case _DRM_REGISTERS:
- offset = dev->driver->get_reg_ofs(dev);
+ offset = drm_core_get_reg_ofs(dev);
vma->vm_flags |= VM_IO; /* not in core dump */
vma->vm_page_prot = drm_io_prot(map->type, vma);
#if !defined(__arm__)
diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
index fe69914ce507..88bcd331e7c5 100644
--- a/drivers/gpu/drm/i810/i810_drv.c
+++ b/drivers/gpu/drm/i810/i810_drv.c
@@ -52,8 +52,6 @@ static struct drm_driver driver = {
.device_is_agp = i810_driver_device_is_agp,
.reclaim_buffers_locked = i810_driver_reclaim_buffers_locked,
.dma_quiescent = i810_driver_dma_quiescent,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = i810_ioctls,
.fops = {
.owner = THIS_MODULE,
diff --git a/drivers/gpu/drm/i830/i830_drv.c b/drivers/gpu/drm/i830/i830_drv.c
index 5b6298b24e24..f655ab7977da 100644
--- a/drivers/gpu/drm/i830/i830_drv.c
+++ b/drivers/gpu/drm/i830/i830_drv.c
@@ -57,8 +57,6 @@ static struct drm_driver driver = {
.device_is_agp = i830_driver_device_is_agp,
.reclaim_buffers_locked = i830_driver_reclaim_buffers_locked,
.dma_quiescent = i830_driver_dma_quiescent,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
#if USE_IRQS
.irq_preinstall = i830_driver_irq_preinstall,
.irq_postinstall = i830_driver_irq_postinstall,
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 5c8e53458edb..fdc833d5cc7b 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -26,15 +26,17 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
intel_dvo.o \
intel_ringbuffer.o \
intel_overlay.o \
+ intel_opregion.o \
dvo_ch7xxx.o \
dvo_ch7017.o \
dvo_ivch.o \
dvo_tfp410.o \
dvo_sil164.o
-i915-$(CONFIG_ACPI) += i915_opregion.o
i915-$(CONFIG_COMPAT) += i915_ioc32.o
+i915-$(CONFIG_ACPI) += intel_acpi.o
+
obj-$(CONFIG_DRM_I915) += i915.o
CFLAGS_i915_trace_points.o := -I$(src)
diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c
index 14d59804acd7..af70337567ce 100644
--- a/drivers/gpu/drm/i915/dvo_ch7017.c
+++ b/drivers/gpu/drm/i915/dvo_ch7017.c
@@ -165,67 +165,44 @@ struct ch7017_priv {
static void ch7017_dump_regs(struct intel_dvo_device *dvo);
static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
-static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
+static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val)
{
- struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
- u8 out_buf[2];
- u8 in_buf[2];
-
struct i2c_msg msgs[] = {
{
.addr = dvo->slave_addr,
.flags = 0,
.len = 1,
- .buf = out_buf,
+ .buf = &addr,
},
{
.addr = dvo->slave_addr,
.flags = I2C_M_RD,
.len = 1,
- .buf = in_buf,
+ .buf = val,
}
};
-
- out_buf[0] = addr;
- out_buf[1] = 0;
-
- if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
- *val= in_buf[0];
- return true;
- };
-
- return false;
+ return i2c_transfer(dvo->i2c_bus, msgs, 2) == 2;
}
-static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
+static bool ch7017_write(struct intel_dvo_device *dvo, u8 addr, u8 val)
{
- struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
- uint8_t out_buf[2];
+ uint8_t buf[2] = { addr, val };
struct i2c_msg msg = {
.addr = dvo->slave_addr,
.flags = 0,
.len = 2,
- .buf = out_buf,
+ .buf = buf,
};
-
- out_buf[0] = addr;
- out_buf[1] = val;
-
- if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
- return true;
-
- return false;
+ return i2c_transfer(dvo->i2c_bus, &msg, 1) == 1;
}
/** Probes for a CH7017 on the given bus and slave address. */
static bool ch7017_init(struct intel_dvo_device *dvo,
struct i2c_adapter *adapter)
{
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
struct ch7017_priv *priv;
- uint8_t val;
+ const char *str;
+ u8 val;
priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);
if (priv == NULL)
@@ -237,16 +214,27 @@ static bool ch7017_init(struct intel_dvo_device *dvo,
if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
goto fail;
- if (val != CH7017_DEVICE_ID_VALUE &&
- val != CH7018_DEVICE_ID_VALUE &&
- val != CH7019_DEVICE_ID_VALUE) {
+ switch (val) {
+ case CH7017_DEVICE_ID_VALUE:
+ str = "ch7017";
+ break;
+ case CH7018_DEVICE_ID_VALUE:
+ str = "ch7018";
+ break;
+ case CH7019_DEVICE_ID_VALUE:
+ str = "ch7019";
+ break;
+ default:
DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
- "Slave %d.\n",
- val, i2cbus->adapter.name,dvo->slave_addr);
+ "slave %d.\n",
+ val, adapter->name,dvo->slave_addr);
goto fail;
}
+ DRM_DEBUG_KMS("%s detected on %s, addr %d\n",
+ str, adapter->name, dvo->slave_addr);
return true;
+
fail:
kfree(priv);
return false;
@@ -368,7 +356,7 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
}
/* XXX: Should actually wait for update power status somehow */
- udelay(20000);
+ msleep(20);
}
static void ch7017_dump_regs(struct intel_dvo_device *dvo)
diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
index 6f1944b24441..7eaa94e4ff06 100644
--- a/drivers/gpu/drm/i915/dvo_ch7xxx.c
+++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
@@ -113,7 +113,6 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[2];
u8 in_buf[2];
@@ -135,14 +134,14 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
out_buf[0] = addr;
out_buf[1] = 0;
- if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};
if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
}
@@ -152,7 +151,6 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
@@ -164,12 +162,12 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
out_buf[0] = addr;
out_buf[1] = ch;
- if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ if (i2c_transfer(adapter, &msg, 1) == 1)
return true;
if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
index a2ec3f487202..a12ed9414cc7 100644
--- a/drivers/gpu/drm/i915/dvo_ivch.c
+++ b/drivers/gpu/drm/i915/dvo_ivch.c
@@ -167,7 +167,6 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
{
struct ivch_priv *priv = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[1];
u8 in_buf[2];
@@ -193,7 +192,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
out_buf[0] = addr;
- if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) {
+ if (i2c_transfer(adapter, msgs, 3) == 3) {
*data = (in_buf[1] << 8) | in_buf[0];
return true;
};
@@ -201,7 +200,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from "
"%s:%02x.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
}
@@ -211,7 +210,6 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
{
struct ivch_priv *priv = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[3];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
@@ -224,12 +222,12 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
out_buf[1] = data & 0xff;
out_buf[2] = data >> 8;
- if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ if (i2c_transfer(adapter, &msg, 1) == 1)
return true;
if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
index 9b8e6765cf26..e4b4091df942 100644
--- a/drivers/gpu/drm/i915/dvo_sil164.c
+++ b/drivers/gpu/drm/i915/dvo_sil164.c
@@ -69,7 +69,6 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct sil164_priv *sil = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[2];
u8 in_buf[2];
@@ -91,14 +90,14 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
out_buf[0] = addr;
out_buf[1] = 0;
- if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};
if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
}
@@ -107,7 +106,6 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct sil164_priv *sil= dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
@@ -119,12 +117,12 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
out_buf[0] = addr;
out_buf[1] = ch;
- if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ if (i2c_transfer(adapter, &msg, 1) == 1)
return true;
if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
index 56f66426207f..8ab2855bb544 100644
--- a/drivers/gpu/drm/i915/dvo_tfp410.c
+++ b/drivers/gpu/drm/i915/dvo_tfp410.c
@@ -94,7 +94,6 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct tfp410_priv *tfp = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[2];
u8 in_buf[2];
@@ -116,14 +115,14 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
out_buf[0] = addr;
out_buf[1] = 0;
- if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};
if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
}
@@ -132,7 +131,6 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct tfp410_priv *tfp = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
- struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
@@ -144,12 +142,12 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
out_buf[0] = addr;
out_buf[1] = ch;
- if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ if (i2c_transfer(adapter, &msg, 1) == 1)
return true;
if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
- addr, i2cbus->adapter.name, dvo->slave_addr);
+ addr, adapter->name, dvo->slave_addr);
}
return false;
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 048149748fdc..1f4f3ceb63c7 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -40,9 +40,51 @@
#if defined(CONFIG_DEBUG_FS)
-#define ACTIVE_LIST 1
-#define FLUSHING_LIST 2
-#define INACTIVE_LIST 3
+enum {
+ ACTIVE_LIST,
+ FLUSHING_LIST,
+ INACTIVE_LIST,
+ PINNED_LIST,
+ DEFERRED_FREE_LIST,
+};
+
+static const char *yesno(int v)
+{
+ return v ? "yes" : "no";
+}
+
+static int i915_capabilities(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ const struct intel_device_info *info = INTEL_INFO(dev);
+
+ seq_printf(m, "gen: %d\n", info->gen);
+#define B(x) seq_printf(m, #x ": %s\n", yesno(info->x))
+ B(is_mobile);
+ B(is_i85x);
+ B(is_i915g);
+ B(is_i945gm);
+ B(is_g33);
+ B(need_gfx_hws);
+ B(is_g4x);
+ B(is_pineview);
+ B(is_broadwater);
+ B(is_crestline);
+ B(has_fbc);
+ B(has_rc6);
+ B(has_pipe_cxsr);
+ B(has_hotplug);
+ B(cursor_needs_physical);
+ B(has_overlay);
+ B(overlay_needs_physical);
+ B(supports_tv);
+ B(has_bsd_ring);
+ B(has_blt_ring);
+#undef B
+
+ return 0;
+}
static const char *get_pin_flag(struct drm_i915_gem_object *obj_priv)
{
@@ -64,6 +106,29 @@ static const char *get_tiling_flag(struct drm_i915_gem_object *obj_priv)
}
}
+static void
+describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
+{
+ seq_printf(m, "%p: %s%s %8zd %08x %08x %d%s%s",
+ &obj->base,
+ get_pin_flag(obj),
+ get_tiling_flag(obj),
+ obj->base.size,
+ obj->base.read_domains,
+ obj->base.write_domain,
+ obj->last_rendering_seqno,
+ obj->dirty ? " dirty" : "",
+ obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
+ if (obj->base.name)
+ seq_printf(m, " (name: %d)", obj->base.name);
+ if (obj->fence_reg != I915_FENCE_REG_NONE)
+ seq_printf(m, " (fence: %d)", obj->fence_reg);
+ if (obj->gtt_space != NULL)
+ seq_printf(m, " (gtt_offset: %08x)", obj->gtt_offset);
+ if (obj->ring != NULL)
+ seq_printf(m, " (%s)", obj->ring->name);
+}
+
static int i915_gem_object_list_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -72,56 +137,80 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv;
- spinlock_t *lock = NULL;
+ size_t total_obj_size, total_gtt_size;
+ int count, ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
switch (list) {
case ACTIVE_LIST:
seq_printf(m, "Active:\n");
- lock = &dev_priv->mm.active_list_lock;
- head = &dev_priv->render_ring.active_list;
+ head = &dev_priv->mm.active_list;
break;
case INACTIVE_LIST:
seq_printf(m, "Inactive:\n");
head = &dev_priv->mm.inactive_list;
break;
+ case PINNED_LIST:
+ seq_printf(m, "Pinned:\n");
+ head = &dev_priv->mm.pinned_list;
+ break;
case FLUSHING_LIST:
seq_printf(m, "Flushing:\n");
head = &dev_priv->mm.flushing_list;
break;
+ case DEFERRED_FREE_LIST:
+ seq_printf(m, "Deferred free:\n");
+ head = &dev_priv->mm.deferred_free_list;
+ break;
default:
- DRM_INFO("Ooops, unexpected list\n");
- return 0;
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
}
- if (lock)
- spin_lock(lock);
- list_for_each_entry(obj_priv, head, list)
- {
- seq_printf(m, " %p: %s %8zd %08x %08x %d%s%s",
- &obj_priv->base,
- get_pin_flag(obj_priv),
- obj_priv->base.size,
- obj_priv->base.read_domains,
- obj_priv->base.write_domain,
- obj_priv->last_rendering_seqno,
- obj_priv->dirty ? " dirty" : "",
- obj_priv->madv == I915_MADV_DONTNEED ? " purgeable" : "");
-
- if (obj_priv->base.name)
- seq_printf(m, " (name: %d)", obj_priv->base.name);
- if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
- seq_printf(m, " (fence: %d)", obj_priv->fence_reg);
- if (obj_priv->gtt_space != NULL)
- seq_printf(m, " (gtt_offset: %08x)", obj_priv->gtt_offset);
-
+ total_obj_size = total_gtt_size = count = 0;
+ list_for_each_entry(obj_priv, head, mm_list) {
+ seq_printf(m, " ");
+ describe_obj(m, obj_priv);
seq_printf(m, "\n");
+ total_obj_size += obj_priv->base.size;
+ total_gtt_size += obj_priv->gtt_space->size;
+ count++;
}
+ mutex_unlock(&dev->struct_mutex);
- if (lock)
- spin_unlock(lock);
+ seq_printf(m, "Total %d objects, %zu bytes, %zu GTT size\n",
+ count, total_obj_size, total_gtt_size);
return 0;
}
+static int i915_gem_object_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;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ seq_printf(m, "%u objects\n", dev_priv->mm.object_count);
+ seq_printf(m, "%zu object bytes\n", dev_priv->mm.object_memory);
+ seq_printf(m, "%u pinned\n", dev_priv->mm.pin_count);
+ seq_printf(m, "%zu pin bytes\n", dev_priv->mm.pin_memory);
+ seq_printf(m, "%u objects in gtt\n", dev_priv->mm.gtt_count);
+ seq_printf(m, "%zu gtt bytes\n", dev_priv->mm.gtt_memory);
+ seq_printf(m, "%zu gtt total\n", dev_priv->mm.gtt_total);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+
static int i915_gem_pageflip_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -176,6 +265,11 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_request *gem_request;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
seq_printf(m, "Request:\n");
list_for_each_entry(gem_request, &dev_priv->render_ring.request_list,
@@ -184,6 +278,8 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
gem_request->seqno,
(int) (jiffies - gem_request->emitted_jiffies));
}
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -192,16 +288,24 @@ static int i915_gem_seqno_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;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
if (dev_priv->render_ring.status_page.page_addr != NULL) {
seq_printf(m, "Current sequence: %d\n",
- i915_get_gem_seqno(dev, &dev_priv->render_ring));
+ dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring));
} else {
seq_printf(m, "Current sequence: hws uninitialized\n");
}
seq_printf(m, "Waiter sequence: %d\n",
dev_priv->mm.waiting_gem_seqno);
seq_printf(m, "IRQ sequence: %d\n", dev_priv->mm.irq_gem_seqno);
+
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -211,6 +315,11 @@ static int i915_interrupt_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;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
if (!HAS_PCH_SPLIT(dev)) {
seq_printf(m, "Interrupt enable: %08x\n",
@@ -247,7 +356,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
atomic_read(&dev_priv->irq_received));
if (dev_priv->render_ring.status_page.page_addr != NULL) {
seq_printf(m, "Current sequence: %d\n",
- i915_get_gem_seqno(dev, &dev_priv->render_ring));
+ dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring));
} else {
seq_printf(m, "Current sequence: hws uninitialized\n");
}
@@ -255,6 +364,8 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
dev_priv->mm.waiting_gem_seqno);
seq_printf(m, "IRQ sequence: %d\n",
dev_priv->mm.irq_gem_seqno);
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -263,7 +374,11 @@ static int i915_gem_fence_regs_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;
- int i;
+ int i, ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
seq_printf(m, "Reserved fences = %d\n", dev_priv->fence_reg_start);
seq_printf(m, "Total fences = %d\n", dev_priv->num_fence_regs);
@@ -289,6 +404,7 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
seq_printf(m, "\n");
}
}
+ mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -313,16 +429,19 @@ static int i915_hws_info(struct seq_file *m, void *data)
return 0;
}
-static void i915_dump_pages(struct seq_file *m, struct page **pages, int page_count)
+static void i915_dump_object(struct seq_file *m,
+ struct io_mapping *mapping,
+ struct drm_i915_gem_object *obj_priv)
{
- int page, i;
- uint32_t *mem;
+ int page, page_count, i;
+ page_count = obj_priv->base.size / PAGE_SIZE;
for (page = 0; page < page_count; page++) {
- mem = kmap_atomic(pages[page], KM_USER0);
+ u32 *mem = io_mapping_map_wc(mapping,
+ obj_priv->gtt_offset + page * PAGE_SIZE);
for (i = 0; i < PAGE_SIZE; i += 4)
seq_printf(m, "%08x : %08x\n", i, mem[i / 4]);
- kunmap_atomic(mem, KM_USER0);
+ io_mapping_unmap(mem);
}
}
@@ -335,27 +454,20 @@ static int i915_batchbuffer_info(struct seq_file *m, void *data)
struct drm_i915_gem_object *obj_priv;
int ret;
- spin_lock(&dev_priv->mm.active_list_lock);
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
- list_for_each_entry(obj_priv, &dev_priv->render_ring.active_list,
- list) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.active_list, mm_list) {
obj = &obj_priv->base;
if (obj->read_domains & I915_GEM_DOMAIN_COMMAND) {
- ret = i915_gem_object_get_pages(obj, 0);
- if (ret) {
- DRM_ERROR("Failed to get pages: %d\n", ret);
- spin_unlock(&dev_priv->mm.active_list_lock);
- return ret;
- }
-
- seq_printf(m, "--- gtt_offset = 0x%08x\n", obj_priv->gtt_offset);
- i915_dump_pages(m, obj_priv->pages, obj->size / PAGE_SIZE);
-
- i915_gem_object_put_pages(obj);
+ seq_printf(m, "--- gtt_offset = 0x%08x\n",
+ obj_priv->gtt_offset);
+ i915_dump_object(m, dev_priv->mm.gtt_mapping, obj_priv);
}
}
- spin_unlock(&dev_priv->mm.active_list_lock);
+ mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -365,20 +477,24 @@ static int i915_ringbuffer_data(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;
- u8 *virt;
- uint32_t *ptr, off;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
if (!dev_priv->render_ring.gem_object) {
seq_printf(m, "No ringbuffer setup\n");
- return 0;
- }
-
- virt = dev_priv->render_ring.virtual_start;
+ } else {
+ u8 *virt = dev_priv->render_ring.virtual_start;
+ uint32_t off;
- for (off = 0; off < dev_priv->render_ring.size; off += 4) {
- ptr = (uint32_t *)(virt + off);
- seq_printf(m, "%08x : %08x\n", off, *ptr);
+ for (off = 0; off < dev_priv->render_ring.size; off += 4) {
+ uint32_t *ptr = (uint32_t *)(virt + off);
+ seq_printf(m, "%08x : %08x\n", off, *ptr);
+ }
}
+ mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -396,7 +512,7 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data)
seq_printf(m, "RingHead : %08x\n", head);
seq_printf(m, "RingTail : %08x\n", tail);
seq_printf(m, "RingSize : %08lx\n", dev_priv->render_ring.size);
- seq_printf(m, "Acthd : %08x\n", I915_READ(IS_I965G(dev) ? ACTHD_I965 : ACTHD));
+ seq_printf(m, "Acthd : %08x\n", I915_READ(INTEL_INFO(dev)->gen >= 4 ? ACTHD_I965 : ACTHD));
return 0;
}
@@ -458,7 +574,7 @@ static int i915_error_state(struct seq_file *m, void *unused)
seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr);
seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone);
seq_printf(m, " ACTHD: 0x%08x\n", error->acthd);
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
seq_printf(m, " INSTPS: 0x%08x\n", error->instps);
seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1);
}
@@ -642,6 +758,9 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
} else {
seq_printf(m, "FBC disabled: ");
switch (dev_priv->no_fbc_reason) {
+ case FBC_NO_OUTPUT:
+ seq_printf(m, "no outputs");
+ break;
case FBC_STOLEN_TOO_SMALL:
seq_printf(m, "not enough stolen memory");
break;
@@ -675,15 +794,17 @@ static int i915_sr_status(struct seq_file *m, void *unused)
drm_i915_private_t *dev_priv = dev->dev_private;
bool sr_enabled = false;
- if (IS_I965GM(dev) || IS_I945G(dev) || IS_I945GM(dev))
+ if (IS_GEN5(dev))
+ sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN;
+ else if (IS_CRESTLINE(dev) || IS_I945G(dev) || IS_I945GM(dev))
sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
else if (IS_I915GM(dev))
sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN;
else if (IS_PINEVIEW(dev))
sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN;
- seq_printf(m, "self-refresh: %s\n", sr_enabled ? "enabled" :
- "disabled");
+ seq_printf(m, "self-refresh: %s\n",
+ sr_enabled ? "enabled" : "disabled");
return 0;
}
@@ -694,10 +815,16 @@ static int i915_emon_status(struct seq_file *m, void *unused)
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
unsigned long temp, chipset, gfx;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
temp = i915_mch_val(dev_priv);
chipset = i915_chipset_val(dev_priv);
gfx = i915_gfx_val(dev_priv);
+ mutex_unlock(&dev->struct_mutex);
seq_printf(m, "GMCH temp: %ld\n", temp);
seq_printf(m, "Chipset power: %ld\n", chipset);
@@ -718,6 +845,68 @@ static int i915_gfxec(struct seq_file *m, void *unused)
return 0;
}
+static int i915_opregion(struct seq_file *m, void *unused)
+{
+ 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 intel_opregion *opregion = &dev_priv->opregion;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ if (opregion->header)
+ seq_write(m, opregion->header, OPREGION_SIZE);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static int i915_gem_framebuffer_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 intel_fbdev *ifbdev;
+ struct intel_framebuffer *fb;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+ if (ret)
+ return ret;
+
+ ifbdev = dev_priv->fbdev;
+ fb = to_intel_framebuffer(ifbdev->helper.fb);
+
+ seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, obj ",
+ fb->base.width,
+ fb->base.height,
+ fb->base.depth,
+ fb->base.bits_per_pixel);
+ describe_obj(m, to_intel_bo(fb->obj));
+ seq_printf(m, "\n");
+
+ list_for_each_entry(fb, &dev->mode_config.fb_list, base.head) {
+ if (&fb->base == ifbdev->helper.fb)
+ continue;
+
+ seq_printf(m, "user size: %d x %d, depth %d, %d bpp, obj ",
+ fb->base.width,
+ fb->base.height,
+ fb->base.depth,
+ fb->base.bits_per_pixel);
+ describe_obj(m, to_intel_bo(fb->obj));
+ seq_printf(m, "\n");
+ }
+
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return 0;
+}
+
static int
i915_wedged_open(struct inode *inode,
struct file *filp)
@@ -741,6 +930,9 @@ i915_wedged_read(struct file *filp,
"wedged : %d\n",
atomic_read(&dev_priv->mm.wedged));
+ if (len > sizeof (buf))
+ len = sizeof (buf);
+
return simple_read_from_buffer(ubuf, max, ppos, buf, len);
}
@@ -770,7 +962,7 @@ i915_wedged_write(struct file *filp,
atomic_set(&dev_priv->mm.wedged, val);
if (val) {
- DRM_WAKEUP(&dev_priv->irq_queue);
+ wake_up_all(&dev_priv->irq_queue);
queue_work(dev_priv->wq, &dev_priv->error_work);
}
@@ -824,9 +1016,13 @@ static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
}
static struct drm_info_list i915_debugfs_list[] = {
+ {"i915_capabilities", i915_capabilities, 0, 0},
+ {"i915_gem_objects", i915_gem_object_info, 0},
{"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
{"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
{"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
+ {"i915_gem_pinned", i915_gem_object_list_info, 0, (void *) PINNED_LIST},
+ {"i915_gem_deferred_free", i915_gem_object_list_info, 0, (void *) DEFERRED_FREE_LIST},
{"i915_gem_pageflip", i915_gem_pageflip_info, 0},
{"i915_gem_request", i915_gem_request_info, 0},
{"i915_gem_seqno", i915_gem_seqno_info, 0},
@@ -846,6 +1042,8 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_gfxec", i915_gfxec, 0},
{"i915_fbc_status", i915_fbc_status, 0},
{"i915_sr_status", i915_sr_status, 0},
+ {"i915_opregion", i915_opregion, 0},
+ {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 2dd2c93ebfa3..7a26f4dd21ae 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -40,8 +40,7 @@
#include <linux/pnp.h>
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
-
-extern int intel_max_stolen; /* from AGP driver */
+#include <acpi/video.h>
/**
* Sets up the hardware status page for devices that need a physical address
@@ -64,7 +63,7 @@ static int i915_init_phys_hws(struct drm_device *dev)
memset(dev_priv->render_ring.status_page.page_addr, 0, PAGE_SIZE);
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
dev_priv->dma_status_page |= (dev_priv->dma_status_page >> 28) &
0xf0;
@@ -133,8 +132,8 @@ static int i915_dma_cleanup(struct drm_device * dev)
mutex_lock(&dev->struct_mutex);
intel_cleanup_ring_buffer(dev, &dev_priv->render_ring);
- if (HAS_BSD(dev))
- intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
+ intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
+ intel_cleanup_ring_buffer(dev, &dev_priv->blt_ring);
mutex_unlock(&dev->struct_mutex);
/* Clear the HWS virtual address at teardown */
@@ -222,7 +221,7 @@ static int i915_dma_resume(struct drm_device * dev)
DRM_DEBUG_DRIVER("hw status page @ %p\n",
ring->status_page.page_addr);
if (ring->status_page.gfx_addr != 0)
- ring->setup_status_page(dev, ring);
+ intel_ring_setup_status_page(dev, ring);
else
I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
@@ -377,7 +376,7 @@ i915_emit_box(struct drm_device *dev,
return -EINVAL;
}
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
BEGIN_LP_RING(4);
OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
@@ -481,7 +480,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
if (!IS_I830(dev) && !IS_845G(dev)) {
BEGIN_LP_RING(2);
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
OUT_RING(MI_BATCH_BUFFER_START | (2 << 6) | MI_BATCH_NON_SECURE_I965);
OUT_RING(batch->start);
} else {
@@ -500,7 +499,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
}
- if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
+ if (IS_G4X(dev) || IS_GEN5(dev)) {
BEGIN_LP_RING(2);
OUT_RING(MI_FLUSH | MI_NO_WRITE_FLUSH | MI_INVALIDATE_ISP);
OUT_RING(MI_NOOP);
@@ -765,6 +764,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_HAS_BSD:
value = HAS_BSD(dev);
break;
+ case I915_PARAM_HAS_BLT:
+ value = HAS_BLT(dev);
+ break;
default:
DRM_DEBUG_DRIVER("Unknown parameter %d\n",
param->param);
@@ -888,12 +890,12 @@ static int
intel_alloc_mchbar_resource(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+ int reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
u32 temp_lo, temp_hi = 0;
u64 mchbar_addr;
int ret;
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
pci_read_config_dword(dev_priv->bridge_dev, reg + 4, &temp_hi);
pci_read_config_dword(dev_priv->bridge_dev, reg, &temp_lo);
mchbar_addr = ((u64)temp_hi << 32) | temp_lo;
@@ -920,7 +922,7 @@ intel_alloc_mchbar_resource(struct drm_device *dev)
return ret;
}
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
pci_write_config_dword(dev_priv->bridge_dev, reg + 4,
upper_32_bits(dev_priv->mch_res.start));
@@ -934,7 +936,7 @@ static void
intel_setup_mchbar(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+ int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
u32 temp;
bool enabled;
@@ -971,7 +973,7 @@ static void
intel_teardown_mchbar(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+ int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
u32 temp;
if (dev_priv->mchbar_need_disable) {
@@ -990,174 +992,6 @@ intel_teardown_mchbar(struct drm_device *dev)
release_resource(&dev_priv->mch_res);
}
-/**
- * i915_probe_agp - get AGP bootup configuration
- * @pdev: PCI device
- * @aperture_size: returns AGP aperture configured size
- * @preallocated_size: returns size of BIOS preallocated AGP space
- *
- * Since Intel integrated graphics are UMA, the BIOS has to set aside
- * some RAM for the framebuffer at early boot. This code figures out
- * how much was set aside so we can use it for our own purposes.
- */
-static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
- uint32_t *preallocated_size,
- uint32_t *start)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- u16 tmp = 0;
- unsigned long overhead;
- unsigned long stolen;
-
- /* Get the fb aperture size and "stolen" memory amount. */
- pci_read_config_word(dev_priv->bridge_dev, INTEL_GMCH_CTRL, &tmp);
-
- *aperture_size = 1024 * 1024;
- *preallocated_size = 1024 * 1024;
-
- switch (dev->pdev->device) {
- case PCI_DEVICE_ID_INTEL_82830_CGC:
- case PCI_DEVICE_ID_INTEL_82845G_IG:
- case PCI_DEVICE_ID_INTEL_82855GM_IG:
- case PCI_DEVICE_ID_INTEL_82865_IG:
- if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M)
- *aperture_size *= 64;
- else
- *aperture_size *= 128;
- break;
- default:
- /* 9xx supports large sizes, just look at the length */
- *aperture_size = pci_resource_len(dev->pdev, 2);
- break;
- }
-
- /*
- * Some of the preallocated space is taken by the GTT
- * and popup. GTT is 1K per MB of aperture size, and popup is 4K.
- */
- if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev))
- overhead = 4096;
- else
- overhead = (*aperture_size / 1024) + 4096;
-
- if (IS_GEN6(dev)) {
- /* SNB has memory control reg at 0x50.w */
- pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &tmp);
-
- switch (tmp & SNB_GMCH_GMS_STOLEN_MASK) {
- case INTEL_855_GMCH_GMS_DISABLED:
- DRM_ERROR("video memory is disabled\n");
- return -1;
- case SNB_GMCH_GMS_STOLEN_32M:
- stolen = 32 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_64M:
- stolen = 64 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_96M:
- stolen = 96 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_128M:
- stolen = 128 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_160M:
- stolen = 160 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_192M:
- stolen = 192 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_224M:
- stolen = 224 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_256M:
- stolen = 256 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_288M:
- stolen = 288 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_320M:
- stolen = 320 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_352M:
- stolen = 352 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_384M:
- stolen = 384 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_416M:
- stolen = 416 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_448M:
- stolen = 448 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_480M:
- stolen = 480 * 1024 * 1024;
- break;
- case SNB_GMCH_GMS_STOLEN_512M:
- stolen = 512 * 1024 * 1024;
- break;
- default:
- DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
- tmp & SNB_GMCH_GMS_STOLEN_MASK);
- return -1;
- }
- } else {
- switch (tmp & INTEL_GMCH_GMS_MASK) {
- case INTEL_855_GMCH_GMS_DISABLED:
- DRM_ERROR("video memory is disabled\n");
- return -1;
- case INTEL_855_GMCH_GMS_STOLEN_1M:
- stolen = 1 * 1024 * 1024;
- break;
- case INTEL_855_GMCH_GMS_STOLEN_4M:
- stolen = 4 * 1024 * 1024;
- break;
- case INTEL_855_GMCH_GMS_STOLEN_8M:
- stolen = 8 * 1024 * 1024;
- break;
- case INTEL_855_GMCH_GMS_STOLEN_16M:
- stolen = 16 * 1024 * 1024;
- break;
- case INTEL_855_GMCH_GMS_STOLEN_32M:
- stolen = 32 * 1024 * 1024;
- break;
- case INTEL_915G_GMCH_GMS_STOLEN_48M:
- stolen = 48 * 1024 * 1024;
- break;
- case INTEL_915G_GMCH_GMS_STOLEN_64M:
- stolen = 64 * 1024 * 1024;
- break;
- case INTEL_GMCH_GMS_STOLEN_128M:
- stolen = 128 * 1024 * 1024;
- break;
- case INTEL_GMCH_GMS_STOLEN_256M:
- stolen = 256 * 1024 * 1024;
- break;
- case INTEL_GMCH_GMS_STOLEN_96M:
- stolen = 96 * 1024 * 1024;
- break;
- case INTEL_GMCH_GMS_STOLEN_160M:
- stolen = 160 * 1024 * 1024;
- break;
- case INTEL_GMCH_GMS_STOLEN_224M:
- stolen = 224 * 1024 * 1024;
- break;
- case INTEL_GMCH_GMS_STOLEN_352M:
- stolen = 352 * 1024 * 1024;
- break;
- default:
- DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
- tmp & INTEL_GMCH_GMS_MASK);
- return -1;
- }
- }
-
- *preallocated_size = stolen - overhead;
- *start = overhead;
-
- return 0;
-}
-
#define PTE_ADDRESS_MASK 0xfffff000
#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */
#define PTE_MAPPING_TYPE_UNCACHED (0 << 1)
@@ -1181,11 +1015,11 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
{
unsigned long *gtt;
unsigned long entry, phys;
- int gtt_bar = IS_I9XX(dev) ? 0 : 1;
+ int gtt_bar = IS_GEN2(dev) ? 1 : 0;
int gtt_offset, gtt_size;
- if (IS_I965G(dev)) {
- if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
+ if (IS_G4X(dev) || INTEL_INFO(dev)->gen > 4) {
gtt_offset = 2*1024*1024;
gtt_size = 2*1024*1024;
} else {
@@ -1210,10 +1044,8 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
/* Mask out these reserved bits on this hardware. */
- if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) ||
- IS_I945G(dev) || IS_I945GM(dev)) {
+ if (INTEL_INFO(dev)->gen < 4 && !IS_G33(dev))
entry &= ~PTE_ADDRESS_MASK_HIGH;
- }
/* If it's not a mapping type we know, then bail. */
if ((entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_UNCACHED &&
@@ -1252,7 +1084,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
unsigned long ll_base = 0;
/* Leave 1M for line length buffer & misc. */
- compressed_fb = drm_mm_search_free(&dev_priv->vram, size, 4096, 0);
+ compressed_fb = drm_mm_search_free(&dev_priv->mm.vram, size, 4096, 0);
if (!compressed_fb) {
dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
i915_warn_stolen(dev);
@@ -1273,7 +1105,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
}
if (!(IS_GM45(dev) || IS_IRONLAKE_M(dev))) {
- compressed_llb = drm_mm_search_free(&dev_priv->vram, 4096,
+ compressed_llb = drm_mm_search_free(&dev_priv->mm.vram, 4096,
4096, 0);
if (!compressed_llb) {
i915_warn_stolen(dev);
@@ -1343,10 +1175,8 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
/* i915 resume handler doesn't set to D0 */
pci_set_power_state(dev->pdev, PCI_D0);
i915_resume(dev);
- drm_kms_helper_poll_enable(dev);
} else {
printk(KERN_ERR "i915: switched off\n");
- drm_kms_helper_poll_disable(dev);
i915_suspend(dev, pmm);
}
}
@@ -1363,23 +1193,14 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
}
static int i915_load_modeset_init(struct drm_device *dev,
- unsigned long prealloc_start,
unsigned long prealloc_size,
unsigned long agp_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int fb_bar = IS_I9XX(dev) ? 2 : 0;
int ret = 0;
- dev->mode_config.fb_base = pci_resource_start(dev->pdev, fb_bar) &
- 0xff000000;
-
- /* Basic memrange allocator for stolen space (aka vram) */
- drm_mm_init(&dev_priv->vram, 0, prealloc_size);
- DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024));
-
- /* We're off and running w/KMS */
- dev_priv->mm.suspended = 0;
+ /* Basic memrange allocator for stolen space (aka mm.vram) */
+ drm_mm_init(&dev_priv->mm.vram, 0, prealloc_size);
/* Let GEM Manage from end of prealloc space to end of aperture.
*
@@ -1414,7 +1235,7 @@ static int i915_load_modeset_init(struct drm_device *dev,
*/
dev_priv->allow_batchbuffer = 1;
- ret = intel_init_bios(dev);
+ ret = intel_parse_bios(dev);
if (ret)
DRM_INFO("failed to find VBIOS tables\n");
@@ -1423,6 +1244,8 @@ static int i915_load_modeset_init(struct drm_device *dev,
if (ret)
goto cleanup_ringbuffer;
+ intel_register_dsm_handler();
+
ret = vga_switcheroo_register_client(dev->pdev,
i915_switcheroo_set_state,
i915_switcheroo_can_switch);
@@ -1443,17 +1266,15 @@ static int i915_load_modeset_init(struct drm_device *dev,
/* FIXME: do pre/post-mode set stuff in core KMS code */
dev->vblank_disable_allowed = 1;
- /*
- * Initialize the hardware status page IRQ location.
- */
-
- I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
-
ret = intel_fbdev_init(dev);
if (ret)
goto cleanup_irq;
drm_kms_helper_poll_init(dev);
+
+ /* We're off and running w/KMS */
+ dev_priv->mm.suspended = 0;
+
return 0;
cleanup_irq:
@@ -1907,7 +1728,7 @@ static struct drm_i915_private *i915_mch_dev;
* - dev_priv->fmax
* - dev_priv->gpu_busy
*/
-DEFINE_SPINLOCK(mchdev_lock);
+static DEFINE_SPINLOCK(mchdev_lock);
/**
* i915_read_mch_val - return value for IPS use
@@ -2062,7 +1883,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
struct drm_i915_private *dev_priv;
resource_size_t base, size;
int ret = 0, mmio_bar;
- uint32_t agp_size, prealloc_size, prealloc_start;
+ uint32_t agp_size, prealloc_size;
/* i915 has 4 more counters */
dev->counters += 4;
dev->types[6] = _DRM_STAT_IRQ;
@@ -2079,7 +1900,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
dev_priv->info = (struct intel_device_info *) flags;
/* Add register map (needed for suspend/resume) */
- mmio_bar = IS_I9XX(dev) ? 0 : 1;
+ mmio_bar = IS_GEN2(dev) ? 1 : 0;
base = pci_resource_start(dev->pdev, mmio_bar);
size = pci_resource_len(dev->pdev, mmio_bar);
@@ -2121,17 +1942,32 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
"performance may suffer.\n");
}
- ret = i915_probe_agp(dev, &agp_size, &prealloc_size, &prealloc_start);
- if (ret)
+ dev_priv->mm.gtt = intel_gtt_get();
+ if (!dev_priv->mm.gtt) {
+ DRM_ERROR("Failed to initialize GTT\n");
+ ret = -ENODEV;
goto out_iomapfree;
-
- if (prealloc_size > intel_max_stolen) {
- DRM_INFO("detected %dM stolen memory, trimming to %dM\n",
- prealloc_size >> 20, intel_max_stolen >> 20);
- prealloc_size = intel_max_stolen;
}
- dev_priv->wq = create_singlethread_workqueue("i915");
+ prealloc_size = dev_priv->mm.gtt->gtt_stolen_entries << PAGE_SHIFT;
+ agp_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
+
+ /* The i915 workqueue is primarily used for batched retirement of
+ * requests (and thus managing bo) once the task has been completed
+ * by the GPU. i915_gem_retire_requests() is called directly when we
+ * need high-priority retirement, such as waiting for an explicit
+ * bo.
+ *
+ * It is also used for periodic low-priority events, such as
+ * idle-timers and hangcheck.
+ *
+ * All tasks on the workqueue are expected to acquire the dev mutex
+ * so there is no point in running more than one instance of the
+ * workqueue at any time: max_active = 1 and NON_REENTRANT.
+ */
+ dev_priv->wq = alloc_workqueue("i915",
+ WQ_UNBOUND | WQ_NON_REENTRANT,
+ 1);
if (dev_priv->wq == NULL) {
DRM_ERROR("Failed to create our workqueue.\n");
ret = -ENOMEM;
@@ -2159,13 +1995,18 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
dev->driver->get_vblank_counter = i915_get_vblank_counter;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
- if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) {
+ if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev)) {
dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
}
/* Try to make sure MCHBAR is enabled before poking at it */
intel_setup_mchbar(dev);
+ intel_setup_gmbus(dev);
+ intel_opregion_setup(dev);
+
+ /* Make sure the bios did its job and set up vital registers */
+ intel_setup_bios(dev);
i915_gem_load(dev);
@@ -2178,7 +2019,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (IS_PINEVIEW(dev))
i915_pineview_get_mem_freq(dev);
- else if (IS_IRONLAKE(dev))
+ else if (IS_GEN5(dev))
i915_ironlake_get_mem_freq(dev);
/* On the 945G/GM, the chipset reports the MSI capability on the
@@ -2212,8 +2053,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
intel_detect_pch(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
- ret = i915_load_modeset_init(dev, prealloc_start,
- prealloc_size, agp_size);
+ ret = i915_load_modeset_init(dev, prealloc_size, agp_size);
if (ret < 0) {
DRM_ERROR("failed to init modeset\n");
goto out_workqueue_free;
@@ -2221,7 +2061,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
}
/* Must be done after probing outputs */
- intel_opregion_init(dev, 0);
+ intel_opregion_init(dev);
+ acpi_video_register();
setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
(unsigned long) dev);
@@ -2231,9 +2072,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
dev_priv->mchdev_lock = &mchdev_lock;
spin_unlock(&mchdev_lock);
- /* XXX Prevent module unload due to memory corruption bugs. */
- __module_get(THIS_MODULE);
-
return 0;
out_workqueue_free:
@@ -2252,15 +2090,20 @@ free_priv:
int i915_driver_unload(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
-
- i915_destroy_error_state(dev);
+ int ret;
spin_lock(&mchdev_lock);
i915_mch_dev = NULL;
spin_unlock(&mchdev_lock);
- destroy_workqueue(dev_priv->wq);
- del_timer_sync(&dev_priv->hangcheck_timer);
+ mutex_lock(&dev->struct_mutex);
+ ret = i915_gpu_idle(dev);
+ if (ret)
+ DRM_ERROR("failed to idle hardware: %d\n", ret);
+ mutex_unlock(&dev->struct_mutex);
+
+ /* Cancel the retire work handler, which should be idle now. */
+ cancel_delayed_work_sync(&dev_priv->mm.retire_work);
io_mapping_free(dev_priv->mm.gtt_mapping);
if (dev_priv->mm.gtt_mtrr >= 0) {
@@ -2269,7 +2112,10 @@ int i915_driver_unload(struct drm_device *dev)
dev_priv->mm.gtt_mtrr = -1;
}
+ acpi_video_unregister();
+
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ intel_fbdev_fini(dev);
intel_modeset_cleanup(dev);
/*
@@ -2281,20 +2127,25 @@ int i915_driver_unload(struct drm_device *dev)
dev_priv->child_dev = NULL;
dev_priv->child_dev_num = 0;
}
- drm_irq_uninstall(dev);
+
vga_switcheroo_unregister_client(dev->pdev);
vga_client_register(dev->pdev, NULL, NULL, NULL);
}
+ /* Free error state after interrupts are fully disabled. */
+ del_timer_sync(&dev_priv->hangcheck_timer);
+ cancel_work_sync(&dev_priv->error_work);
+ i915_destroy_error_state(dev);
+
if (dev->pdev->msi_enabled)
pci_disable_msi(dev->pdev);
- if (dev_priv->regs != NULL)
- iounmap(dev_priv->regs);
-
- intel_opregion_free(dev, 0);
+ intel_opregion_fini(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ /* Flush any outstanding unpin_work. */
+ flush_workqueue(dev_priv->wq);
+
i915_gem_free_all_phys_object(dev);
mutex_lock(&dev->struct_mutex);
@@ -2302,34 +2153,41 @@ int i915_driver_unload(struct drm_device *dev)
mutex_unlock(&dev->struct_mutex);
if (I915_HAS_FBC(dev) && i915_powersave)
i915_cleanup_compression(dev);
- drm_mm_takedown(&dev_priv->vram);
- i915_gem_lastclose(dev);
+ drm_mm_takedown(&dev_priv->mm.vram);
intel_cleanup_overlay(dev);
+
+ if (!I915_NEED_GFX_HWS(dev))
+ i915_free_hws(dev);
}
+ if (dev_priv->regs != NULL)
+ iounmap(dev_priv->regs);
+
+ intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);
+ destroy_workqueue(dev_priv->wq);
+
pci_dev_put(dev_priv->bridge_dev);
kfree(dev->dev_private);
return 0;
}
-int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv)
+int i915_driver_open(struct drm_device *dev, struct drm_file *file)
{
- struct drm_i915_file_private *i915_file_priv;
+ struct drm_i915_file_private *file_priv;
DRM_DEBUG_DRIVER("\n");
- i915_file_priv = (struct drm_i915_file_private *)
- kmalloc(sizeof(*i915_file_priv), GFP_KERNEL);
-
- if (!i915_file_priv)
+ file_priv = kmalloc(sizeof(*file_priv), GFP_KERNEL);
+ if (!file_priv)
return -ENOMEM;
- file_priv->driver_priv = i915_file_priv;
+ file->driver_priv = file_priv;
- INIT_LIST_HEAD(&i915_file_priv->mm.request_list);
+ spin_lock_init(&file_priv->mm.lock);
+ INIT_LIST_HEAD(&file_priv->mm.request_list);
return 0;
}
@@ -2372,11 +2230,11 @@ void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
i915_mem_release(dev, file_priv, dev_priv->agp_heap);
}
-void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv)
+void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
{
- struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
- kfree(i915_file_priv);
+ kfree(file_priv);
}
struct drm_ioctl_desc i915_ioctls[] = {
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 895ab896e336..3467dd420760 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -32,6 +32,7 @@
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
+#include "intel_drv.h"
#include <linux/console.h>
#include "drm_crtc_helper.h"
@@ -61,86 +62,110 @@ extern int intel_agp_enabled;
.driver_data = (unsigned long) info }
static const struct intel_device_info intel_i830_info = {
- .gen = 2, .is_i8xx = 1, .is_mobile = 1, .cursor_needs_physical = 1,
+ .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1,
+ .has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_845g_info = {
- .gen = 2, .is_i8xx = 1,
+ .gen = 2,
+ .has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i85x_info = {
- .gen = 2, .is_i8xx = 1, .is_i85x = 1, .is_mobile = 1,
+ .gen = 2, .is_i85x = 1, .is_mobile = 1,
.cursor_needs_physical = 1,
+ .has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i865g_info = {
- .gen = 2, .is_i8xx = 1,
+ .gen = 2,
+ .has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i915g_info = {
- .gen = 3, .is_i915g = 1, .is_i9xx = 1, .cursor_needs_physical = 1,
+ .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1,
+ .has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i915gm_info = {
- .gen = 3, .is_i9xx = 1, .is_mobile = 1,
+ .gen = 3, .is_mobile = 1,
.cursor_needs_physical = 1,
+ .has_overlay = 1, .overlay_needs_physical = 1,
+ .supports_tv = 1,
};
static const struct intel_device_info intel_i945g_info = {
- .gen = 3, .is_i9xx = 1, .has_hotplug = 1, .cursor_needs_physical = 1,
+ .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1,
+ .has_overlay = 1, .overlay_needs_physical = 1,
};
static const struct intel_device_info intel_i945gm_info = {
- .gen = 3, .is_i945gm = 1, .is_i9xx = 1, .is_mobile = 1,
+ .gen = 3, .is_i945gm = 1, .is_mobile = 1,
.has_hotplug = 1, .cursor_needs_physical = 1,
+ .has_overlay = 1, .overlay_needs_physical = 1,
+ .supports_tv = 1,
};
static const struct intel_device_info intel_i965g_info = {
- .gen = 4, .is_broadwater = 1, .is_i965g = 1, .is_i9xx = 1,
+ .gen = 4, .is_broadwater = 1,
.has_hotplug = 1,
+ .has_overlay = 1,
};
static const struct intel_device_info intel_i965gm_info = {
- .gen = 4, .is_crestline = 1, .is_i965g = 1, .is_i965gm = 1, .is_i9xx = 1,
+ .gen = 4, .is_crestline = 1,
.is_mobile = 1, .has_fbc = 1, .has_rc6 = 1, .has_hotplug = 1,
+ .has_overlay = 1,
+ .supports_tv = 1,
};
static const struct intel_device_info intel_g33_info = {
- .gen = 3, .is_g33 = 1, .is_i9xx = 1,
+ .gen = 3, .is_g33 = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
+ .has_overlay = 1,
};
static const struct intel_device_info intel_g45_info = {
- .gen = 4, .is_i965g = 1, .is_g4x = 1, .is_i9xx = 1, .need_gfx_hws = 1,
+ .gen = 4, .is_g4x = 1, .need_gfx_hws = 1,
.has_pipe_cxsr = 1, .has_hotplug = 1,
+ .has_bsd_ring = 1,
};
static const struct intel_device_info intel_gm45_info = {
- .gen = 4, .is_i965g = 1, .is_g4x = 1, .is_i9xx = 1,
+ .gen = 4, .is_g4x = 1,
.is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_rc6 = 1,
.has_pipe_cxsr = 1, .has_hotplug = 1,
+ .supports_tv = 1,
+ .has_bsd_ring = 1,
};
static const struct intel_device_info intel_pineview_info = {
- .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .is_i9xx = 1,
+ .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
+ .has_overlay = 1,
};
static const struct intel_device_info intel_ironlake_d_info = {
- .gen = 5, .is_ironlake = 1, .is_i965g = 1, .is_i9xx = 1,
+ .gen = 5,
.need_gfx_hws = 1, .has_pipe_cxsr = 1, .has_hotplug = 1,
+ .has_bsd_ring = 1,
};
static const struct intel_device_info intel_ironlake_m_info = {
- .gen = 5, .is_ironlake = 1, .is_mobile = 1, .is_i965g = 1, .is_i9xx = 1,
+ .gen = 5, .is_mobile = 1,
.need_gfx_hws = 1, .has_fbc = 1, .has_rc6 = 1, .has_hotplug = 1,
+ .has_bsd_ring = 1,
};
static const struct intel_device_info intel_sandybridge_d_info = {
- .gen = 6, .is_i965g = 1, .is_i9xx = 1,
+ .gen = 6,
.need_gfx_hws = 1, .has_hotplug = 1,
+ .has_bsd_ring = 1,
+ .has_blt_ring = 1,
};
static const struct intel_device_info intel_sandybridge_m_info = {
- .gen = 6, .is_i965g = 1, .is_mobile = 1, .is_i9xx = 1,
+ .gen = 6, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
+ .has_bsd_ring = 1,
+ .has_blt_ring = 1,
};
static const struct pci_device_id pciidlist[] = { /* aka */
@@ -237,7 +262,7 @@ static int i915_drm_freeze(struct drm_device *dev)
i915_save_state(dev);
- intel_opregion_free(dev, 1);
+ intel_opregion_fini(dev);
/* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0;
@@ -258,6 +283,8 @@ int i915_suspend(struct drm_device *dev, pm_message_t state)
if (state.event == PM_EVENT_PRETHAW)
return 0;
+ drm_kms_helper_poll_disable(dev);
+
error = i915_drm_freeze(dev);
if (error)
return error;
@@ -277,8 +304,7 @@ static int i915_drm_thaw(struct drm_device *dev)
int error = 0;
i915_restore_state(dev);
-
- intel_opregion_init(dev, 1);
+ intel_opregion_setup(dev);
/* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
@@ -294,6 +320,8 @@ static int i915_drm_thaw(struct drm_device *dev)
drm_helper_resume_force_mode(dev);
}
+ intel_opregion_init(dev);
+
dev_priv->modeset_on_lid = 0;
return error;
@@ -301,12 +329,79 @@ static int i915_drm_thaw(struct drm_device *dev)
int i915_resume(struct drm_device *dev)
{
+ int ret;
+
if (pci_enable_device(dev->pdev))
return -EIO;
pci_set_master(dev->pdev);
- return i915_drm_thaw(dev);
+ ret = i915_drm_thaw(dev);
+ if (ret)
+ return ret;
+
+ drm_kms_helper_poll_enable(dev);
+ return 0;
+}
+
+static int i8xx_do_reset(struct drm_device *dev, u8 flags)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (IS_I85X(dev))
+ return -ENODEV;
+
+ I915_WRITE(D_STATE, I915_READ(D_STATE) | DSTATE_GFX_RESET_I830);
+ POSTING_READ(D_STATE);
+
+ if (IS_I830(dev) || IS_845G(dev)) {
+ I915_WRITE(DEBUG_RESET_I830,
+ DEBUG_RESET_DISPLAY |
+ DEBUG_RESET_RENDER |
+ DEBUG_RESET_FULL);
+ POSTING_READ(DEBUG_RESET_I830);
+ msleep(1);
+
+ I915_WRITE(DEBUG_RESET_I830, 0);
+ POSTING_READ(DEBUG_RESET_I830);
+ }
+
+ msleep(1);
+
+ I915_WRITE(D_STATE, I915_READ(D_STATE) & ~DSTATE_GFX_RESET_I830);
+ POSTING_READ(D_STATE);
+
+ return 0;
+}
+
+static int i965_reset_complete(struct drm_device *dev)
+{
+ u8 gdrst;
+ pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst);
+ return gdrst & 0x1;
+}
+
+static int i965_do_reset(struct drm_device *dev, u8 flags)
+{
+ u8 gdrst;
+
+ /*
+ * Set the domains we want to reset (GRDOM/bits 2 and 3) as
+ * well as the reset bit (GR/bit 0). Setting the GR bit
+ * triggers the reset; when done, the hardware will clear it.
+ */
+ pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst);
+ pci_write_config_byte(dev->pdev, I965_GDRST, gdrst | flags | 0x1);
+
+ return wait_for(i965_reset_complete(dev), 500);
+}
+
+static int ironlake_do_reset(struct drm_device *dev, u8 flags)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
+ I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | flags | 0x1);
+ return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
}
/**
@@ -325,54 +420,39 @@ int i915_resume(struct drm_device *dev)
* - re-init interrupt state
* - re-init display
*/
-int i965_reset(struct drm_device *dev, u8 flags)
+int i915_reset(struct drm_device *dev, u8 flags)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- unsigned long timeout;
- u8 gdrst;
/*
* We really should only reset the display subsystem if we actually
* need to
*/
bool need_display = true;
+ int ret;
mutex_lock(&dev->struct_mutex);
- /*
- * Clear request list
- */
- i915_gem_retire_requests(dev);
-
- if (need_display)
- i915_save_display(dev);
-
- if (IS_I965G(dev) || IS_G4X(dev)) {
- /*
- * Set the domains we want to reset, then the reset bit (bit 0).
- * Clear the reset bit after a while and wait for hardware status
- * bit (bit 1) to be set
- */
- pci_read_config_byte(dev->pdev, GDRST, &gdrst);
- pci_write_config_byte(dev->pdev, GDRST, gdrst | flags | ((flags == GDRST_FULL) ? 0x1 : 0x0));
- udelay(50);
- pci_write_config_byte(dev->pdev, GDRST, gdrst & 0xfe);
-
- /* ...we don't want to loop forever though, 500ms should be plenty */
- timeout = jiffies + msecs_to_jiffies(500);
- do {
- udelay(100);
- pci_read_config_byte(dev->pdev, GDRST, &gdrst);
- } while ((gdrst & 0x1) && time_after(timeout, jiffies));
-
- if (gdrst & 0x1) {
- WARN(true, "i915: Failed to reset chip\n");
- mutex_unlock(&dev->struct_mutex);
- return -EIO;
- }
- } else {
- DRM_ERROR("Error occurred. Don't know how to reset this chip.\n");
+ i915_gem_reset(dev);
+
+ ret = -ENODEV;
+ if (get_seconds() - dev_priv->last_gpu_reset < 5) {
+ DRM_ERROR("GPU hanging too fast, declaring wedged!\n");
+ } else switch (INTEL_INFO(dev)->gen) {
+ case 5:
+ ret = ironlake_do_reset(dev, flags);
+ break;
+ case 4:
+ ret = i965_do_reset(dev, flags);
+ break;
+ case 2:
+ ret = i8xx_do_reset(dev, flags);
+ break;
+ }
+ dev_priv->last_gpu_reset = get_seconds();
+ if (ret) {
+ DRM_ERROR("Failed to reset chip.\n");
mutex_unlock(&dev->struct_mutex);
- return -ENODEV;
+ return ret;
}
/* Ok, now get things going again... */
@@ -400,13 +480,19 @@ int i965_reset(struct drm_device *dev, u8 flags)
mutex_lock(&dev->struct_mutex);
}
+ mutex_unlock(&dev->struct_mutex);
+
/*
- * Display needs restore too...
+ * Perform a full modeset as on later generations, e.g. Ironlake, we may
+ * need to retrain the display link and cannot just restore the register
+ * values.
*/
- if (need_display)
- i915_restore_display(dev);
+ if (need_display) {
+ mutex_lock(&dev->mode_config.mutex);
+ drm_helper_resume_force_mode(dev);
+ mutex_unlock(&dev->mode_config.mutex);
+ }
- mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -524,8 +610,6 @@ static struct drm_driver driver = {
.irq_uninstall = i915_driver_irq_uninstall,
.irq_handler = i915_driver_irq_handler,
.reclaim_buffers = drm_core_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.master_create = i915_master_create,
.master_destroy = i915_master_destroy,
#if defined(CONFIG_DEBUG_FS)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index af4a263cf257..2c2c19b6285e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -34,6 +34,8 @@
#include "intel_bios.h"
#include "intel_ringbuffer.h"
#include <linux/io-mapping.h>
+#include <linux/i2c.h>
+#include <drm/intel-gtt.h>
/* General customization:
*/
@@ -73,11 +75,9 @@ enum plane {
#define DRIVER_PATCHLEVEL 0
#define WATCH_COHERENCY 0
-#define WATCH_BUF 0
#define WATCH_EXEC 0
-#define WATCH_LRU 0
#define WATCH_RELOC 0
-#define WATCH_INACTIVE 0
+#define WATCH_LISTS 0
#define WATCH_PWRITE 0
#define I915_GEM_PHYS_CURSOR_0 1
@@ -110,8 +110,9 @@ struct intel_opregion {
struct opregion_acpi *acpi;
struct opregion_swsci *swsci;
struct opregion_asle *asle;
- int enabled;
+ void *vbt;
};
+#define OPREGION_SIZE (8*1024)
struct intel_overlay;
struct intel_overlay_error_state;
@@ -125,13 +126,16 @@ struct drm_i915_master_private {
struct drm_i915_fence_reg {
struct drm_gem_object *obj;
struct list_head lru_list;
+ bool gpu;
};
struct sdvo_device_mapping {
+ u8 initialized;
u8 dvo_port;
u8 slave_addr;
u8 dvo_wiring;
- u8 initialized;
+ u8 i2c_pin;
+ u8 i2c_speed;
u8 ddc_pin;
};
@@ -193,28 +197,29 @@ struct drm_i915_display_funcs {
struct intel_device_info {
u8 gen;
u8 is_mobile : 1;
- u8 is_i8xx : 1;
u8 is_i85x : 1;
u8 is_i915g : 1;
- u8 is_i9xx : 1;
u8 is_i945gm : 1;
- u8 is_i965g : 1;
- u8 is_i965gm : 1;
u8 is_g33 : 1;
u8 need_gfx_hws : 1;
u8 is_g4x : 1;
u8 is_pineview : 1;
u8 is_broadwater : 1;
u8 is_crestline : 1;
- u8 is_ironlake : 1;
u8 has_fbc : 1;
u8 has_rc6 : 1;
u8 has_pipe_cxsr : 1;
u8 has_hotplug : 1;
u8 cursor_needs_physical : 1;
+ u8 has_overlay : 1;
+ u8 overlay_needs_physical : 1;
+ u8 supports_tv : 1;
+ u8 has_bsd_ring : 1;
+ u8 has_blt_ring : 1;
};
enum no_fbc_reason {
+ FBC_NO_OUTPUT, /* no outputs enabled to compress */
FBC_STOLEN_TOO_SMALL, /* not enough space to hold compressed buffers */
FBC_UNSUPPORTED_MODE, /* interlace or doublescanned mode */
FBC_MODE_TOO_LARGE, /* mode too large for compression */
@@ -241,9 +246,16 @@ typedef struct drm_i915_private {
void __iomem *regs;
+ struct intel_gmbus {
+ struct i2c_adapter adapter;
+ struct i2c_adapter *force_bit;
+ u32 reg0;
+ } *gmbus;
+
struct pci_dev *bridge_dev;
struct intel_ring_buffer render_ring;
struct intel_ring_buffer bsd_ring;
+ struct intel_ring_buffer blt_ring;
uint32_t next_seqno;
drm_dma_handle_t *status_page_dmah;
@@ -263,6 +275,9 @@ typedef struct drm_i915_private {
int front_offset;
int current_page;
int page_flipping;
+#define I915_DEBUG_READ (1<<0)
+#define I915_DEBUG_WRITE (1<<1)
+ unsigned long debug_flags;
wait_queue_head_t irq_queue;
atomic_t irq_received;
@@ -289,24 +304,21 @@ typedef struct drm_i915_private {
unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
int vblank_pipe;
int num_pipe;
- u32 flush_rings;
-#define FLUSH_RENDER_RING 0x1
-#define FLUSH_BSD_RING 0x2
/* For hangcheck timer */
-#define DRM_I915_HANGCHECK_PERIOD 75 /* in jiffies */
+#define DRM_I915_HANGCHECK_PERIOD 250 /* in ms */
struct timer_list hangcheck_timer;
int hangcheck_count;
uint32_t last_acthd;
uint32_t last_instdone;
uint32_t last_instdone1;
- struct drm_mm vram;
-
unsigned long cfb_size;
unsigned long cfb_pitch;
+ unsigned long cfb_offset;
int cfb_fence;
int cfb_plane;
+ int cfb_y;
int irq_enabled;
@@ -316,8 +328,7 @@ typedef struct drm_i915_private {
struct intel_overlay *overlay;
/* LVDS info */
- int backlight_duty_cycle; /* restore backlight to this value */
- bool panel_wants_dither;
+ int backlight_level; /* restore backlight to this value */
struct drm_display_mode *panel_fixed_mode;
struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
@@ -328,13 +339,23 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1;
unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1;
- unsigned int edp_support:1;
int lvds_ssc_freq;
- int edp_bpp;
+ struct {
+ int rate;
+ int lanes;
+ int preemphasis;
+ int vswing;
+
+ bool initialized;
+ bool support;
+ int bpp;
+ struct edp_power_seq pps;
+ } edp;
+ bool no_aux_handshake;
struct notifier_block lid_notifier;
- int crt_ddc_bus; /* 0 = unknown, else GPIO to use for CRT DDC */
+ int crt_ddc_pin;
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
@@ -344,6 +365,7 @@ typedef struct drm_i915_private {
spinlock_t error_lock;
struct drm_i915_error_state *first_error;
struct work_struct error_work;
+ struct completion error_completion;
struct workqueue_struct *wq;
/* Display functions */
@@ -507,6 +529,11 @@ typedef struct drm_i915_private {
u32 saveMCHBAR_RENDER_STANDBY;
struct {
+ /** Bridge to intel-gtt-ko */
+ struct intel_gtt *gtt;
+ /** Memory allocator for GTT stolen memory */
+ struct drm_mm vram;
+ /** Memory allocator for GTT */
struct drm_mm gtt_space;
struct io_mapping *gtt_mapping;
@@ -521,7 +548,16 @@ typedef struct drm_i915_private {
*/
struct list_head shrink_list;
- spinlock_t active_list_lock;
+ /**
+ * List of objects currently involved in rendering.
+ *
+ * Includes buffers having the contents of their GPU caches
+ * flushed, not necessarily primitives. last_rendering_seqno
+ * represents when the rendering involved will be completed.
+ *
+ * A reference is held on the buffer while on this list.
+ */
+ struct list_head active_list;
/**
* List of objects which are not in the ringbuffer but which
@@ -535,15 +571,6 @@ typedef struct drm_i915_private {
struct list_head flushing_list;
/**
- * List of objects currently pending a GPU write flush.
- *
- * All elements on this list will belong to either the
- * active_list or flushing_list, last_rendering_seqno can
- * be used to differentiate between the two elements.
- */
- struct list_head gpu_write_list;
-
- /**
* LRU list of objects which are not in the ringbuffer and
* are ready to unbind, but are still in the GTT.
*
@@ -555,6 +582,12 @@ typedef struct drm_i915_private {
*/
struct list_head inactive_list;
+ /**
+ * LRU list of objects which are not in the ringbuffer but
+ * are still pinned in the GTT.
+ */
+ struct list_head pinned_list;
+
/** LRU list of objects with fence regs on them. */
struct list_head fence_list;
@@ -611,6 +644,17 @@ typedef struct drm_i915_private {
/* storage for physical objects */
struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
+
+ uint32_t flush_rings;
+
+ /* accounting, useful for userland debugging */
+ size_t object_memory;
+ size_t pin_memory;
+ size_t gtt_memory;
+ size_t gtt_total;
+ u32 object_count;
+ u32 pin_count;
+ u32 gtt_count;
} mm;
struct sdvo_device_mapping sdvo_mappings[2];
/* indicate whether the LVDS_BORDER should be enabled or not */
@@ -626,8 +670,6 @@ typedef struct drm_i915_private {
/* Reclocking support */
bool render_reclock_avail;
bool lvds_downclock_avail;
- /* indicate whether the LVDS EDID is OK */
- bool lvds_edid_good;
/* indicates the reduced downclock for LVDS*/
int lvds_downclock;
struct work_struct idle_work;
@@ -661,6 +703,8 @@ typedef struct drm_i915_private {
struct drm_mm_node *compressed_fb;
struct drm_mm_node *compressed_llb;
+ unsigned long last_gpu_reset;
+
/* list of fbdev register on this device */
struct intel_fbdev *fbdev;
} drm_i915_private_t;
@@ -673,7 +717,8 @@ struct drm_i915_gem_object {
struct drm_mm_node *gtt_space;
/** This object's place on the active/flushing/inactive lists */
- struct list_head list;
+ struct list_head ring_list;
+ struct list_head mm_list;
/** This object's place on GPU write list */
struct list_head gpu_write_list;
/** This object's place on eviction list */
@@ -816,12 +861,14 @@ struct drm_i915_gem_request {
/** global list entry for this request */
struct list_head list;
+ struct drm_i915_file_private *file_priv;
/** file_priv list entry for this request */
struct list_head client_list;
};
struct drm_i915_file_private {
struct {
+ struct spinlock lock;
struct list_head request_list;
} mm;
};
@@ -862,7 +909,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
extern int i915_emit_box(struct drm_device *dev,
struct drm_clip_rect *boxes,
int i, int DR1, int DR4);
-extern int i965_reset(struct drm_device *dev, u8 flags);
+extern int i915_reset(struct drm_device *dev, u8 flags);
extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv);
extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv);
extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
@@ -871,7 +918,6 @@ extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
/* i915_irq.c */
void i915_hangcheck_elapsed(unsigned long data);
-void i915_destroy_error_state(struct drm_device *dev);
extern int i915_irq_emit(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int i915_irq_wait(struct drm_device *dev, void *data,
@@ -908,6 +954,12 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
void intel_enable_asle (struct drm_device *dev);
+#ifdef CONFIG_DEBUG_FS
+extern void i915_destroy_error_state(struct drm_device *dev);
+#else
+#define i915_destroy_error_state(x)
+#endif
+
/* i915_mem.c */
extern int i915_mem_alloc(struct drm_device *dev, void *data,
@@ -922,6 +974,7 @@ 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_check_is_wedged(struct drm_device *dev);
int i915_gem_init_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_create_ioctl(struct drm_device *dev, void *data,
@@ -972,13 +1025,22 @@ void i915_gem_object_unpin(struct drm_gem_object *obj);
int i915_gem_object_unbind(struct drm_gem_object *obj);
void i915_gem_release_mmap(struct drm_gem_object *obj);
void i915_gem_lastclose(struct drm_device *dev);
-uint32_t i915_get_gem_seqno(struct drm_device *dev,
- struct intel_ring_buffer *ring);
-bool i915_seqno_passed(uint32_t seq1, uint32_t seq2);
-int i915_gem_object_get_fence_reg(struct drm_gem_object *obj);
-int i915_gem_object_put_fence_reg(struct drm_gem_object *obj);
+
+/**
+ * Returns true if seq1 is later than seq2.
+ */
+static inline bool
+i915_seqno_passed(uint32_t seq1, uint32_t seq2)
+{
+ return (int32_t)(seq1 - seq2) >= 0;
+}
+
+int i915_gem_object_get_fence_reg(struct drm_gem_object *obj,
+ bool interruptible);
+int i915_gem_object_put_fence_reg(struct drm_gem_object *obj,
+ bool interruptible);
void i915_gem_retire_requests(struct drm_device *dev);
-void i915_gem_retire_work_handler(struct work_struct *work);
+void i915_gem_reset(struct drm_device *dev);
void i915_gem_clflush_object(struct drm_gem_object *obj);
int i915_gem_object_set_domain(struct drm_gem_object *obj,
uint32_t read_domains,
@@ -990,16 +1052,18 @@ int i915_gem_do_init(struct drm_device *dev, unsigned long start,
int i915_gpu_idle(struct drm_device *dev);
int i915_gem_idle(struct drm_device *dev);
uint32_t i915_add_request(struct drm_device *dev,
- struct drm_file *file_priv,
- uint32_t flush_domains,
- struct intel_ring_buffer *ring);
+ struct drm_file *file_priv,
+ struct drm_i915_gem_request *request,
+ struct intel_ring_buffer *ring);
int i915_do_wait_request(struct drm_device *dev,
- uint32_t seqno, int interruptible,
- struct intel_ring_buffer *ring);
+ uint32_t seqno,
+ bool interruptible,
+ struct intel_ring_buffer *ring);
int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
int write);
-int i915_gem_object_set_to_display_plane(struct drm_gem_object *obj);
+int i915_gem_object_set_to_display_plane(struct drm_gem_object *obj,
+ bool pipelined);
int i915_gem_attach_phys_object(struct drm_device *dev,
struct drm_gem_object *obj,
int id,
@@ -1007,10 +1071,7 @@ int i915_gem_attach_phys_object(struct drm_device *dev,
void i915_gem_detach_phys_object(struct drm_device *dev,
struct drm_gem_object *obj);
void i915_gem_free_all_phys_object(struct drm_device *dev);
-int i915_gem_object_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
-void i915_gem_object_put_pages(struct drm_gem_object *obj);
void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv);
-int i915_gem_object_flush_write_domain(struct drm_gem_object *obj);
void i915_gem_shrinker_init(void);
void i915_gem_shrinker_exit(void);
@@ -1032,15 +1093,14 @@ bool i915_gem_object_fence_offset_ok(struct drm_gem_object *obj,
/* i915_gem_debug.c */
void i915_gem_dump_object(struct drm_gem_object *obj, int len,
const char *where, uint32_t mark);
-#if WATCH_INACTIVE
-void i915_verify_inactive(struct drm_device *dev, char *file, int line);
+#if WATCH_LISTS
+int i915_verify_lists(struct drm_device *dev);
#else
-#define i915_verify_inactive(dev, file, line)
+#define i915_verify_lists(dev) 0
#endif
void i915_gem_object_check_coherency(struct drm_gem_object *obj, int handle);
void i915_gem_dump_object(struct drm_gem_object *obj, int len,
const char *where, uint32_t mark);
-void i915_dump_lru(struct drm_device *dev, const char *where);
/* i915_debugfs.c */
int i915_debugfs_init(struct drm_minor *minor);
@@ -1054,21 +1114,42 @@ extern int i915_restore_state(struct drm_device *dev);
extern int i915_save_state(struct drm_device *dev);
extern int i915_restore_state(struct drm_device *dev);
+/* intel_i2c.c */
+extern int intel_setup_gmbus(struct drm_device *dev);
+extern void intel_teardown_gmbus(struct drm_device *dev);
+extern void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed);
+extern void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit);
+extern inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter)
+{
+ return container_of(adapter, struct intel_gmbus, adapter)->force_bit;
+}
+extern void intel_i2c_reset(struct drm_device *dev);
+
+/* intel_opregion.c */
+extern int intel_opregion_setup(struct drm_device *dev);
#ifdef CONFIG_ACPI
-/* i915_opregion.c */
-extern int intel_opregion_init(struct drm_device *dev, int resume);
-extern void intel_opregion_free(struct drm_device *dev, int suspend);
-extern void opregion_asle_intr(struct drm_device *dev);
-extern void ironlake_opregion_gse_intr(struct drm_device *dev);
-extern void opregion_enable_asle(struct drm_device *dev);
+extern void intel_opregion_init(struct drm_device *dev);
+extern void intel_opregion_fini(struct drm_device *dev);
+extern void intel_opregion_asle_intr(struct drm_device *dev);
+extern void intel_opregion_gse_intr(struct drm_device *dev);
+extern void intel_opregion_enable_asle(struct drm_device *dev);
#else
-static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; }
-static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; }
-static inline void opregion_asle_intr(struct drm_device *dev) { return; }
-static inline void ironlake_opregion_gse_intr(struct drm_device *dev) { return; }
-static inline void opregion_enable_asle(struct drm_device *dev) { return; }
+static inline void intel_opregion_init(struct drm_device *dev) { return; }
+static inline void intel_opregion_fini(struct drm_device *dev) { return; }
+static inline void intel_opregion_asle_intr(struct drm_device *dev) { return; }
+static inline void intel_opregion_gse_intr(struct drm_device *dev) { return; }
+static inline void intel_opregion_enable_asle(struct drm_device *dev) { return; }
#endif
+/* intel_acpi.c */
+#ifdef CONFIG_ACPI
+extern void intel_register_dsm_handler(void);
+extern void intel_unregister_dsm_handler(void);
+#else
+static inline void intel_register_dsm_handler(void) { return; }
+static inline void intel_unregister_dsm_handler(void) { return; }
+#endif /* CONFIG_ACPI */
+
/* modesetting */
extern void intel_modeset_init(struct drm_device *dev);
extern void intel_modeset_cleanup(struct drm_device *dev);
@@ -1084,8 +1165,10 @@ extern void intel_detect_pch (struct drm_device *dev);
extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
/* overlay */
+#ifdef CONFIG_DEBUG_FS
extern struct intel_overlay_error_state *intel_overlay_capture_error_state(struct drm_device *dev);
extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_overlay_error_state *error);
+#endif
/**
* Lock test for when it's just for synchronization of ring access.
@@ -1099,8 +1182,26 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove
LOCK_TEST_WITH_RETURN(dev, file_priv); \
} while (0)
-#define I915_READ(reg) readl(dev_priv->regs + (reg))
-#define I915_WRITE(reg, val) writel(val, dev_priv->regs + (reg))
+static inline u32 i915_read(struct drm_i915_private *dev_priv, u32 reg)
+{
+ u32 val;
+
+ val = readl(dev_priv->regs + reg);
+ if (dev_priv->debug_flags & I915_DEBUG_READ)
+ printk(KERN_ERR "read 0x%08x from 0x%08x\n", val, reg);
+ return val;
+}
+
+static inline void i915_write(struct drm_i915_private *dev_priv, u32 reg,
+ u32 val)
+{
+ writel(val, dev_priv->regs + reg);
+ if (dev_priv->debug_flags & I915_DEBUG_WRITE)
+ printk(KERN_ERR "wrote 0x%08x to 0x%08x\n", val, reg);
+}
+
+#define I915_READ(reg) i915_read(dev_priv, (reg))
+#define I915_WRITE(reg, val) i915_write(dev_priv, (reg), (val))
#define I915_READ16(reg) readw(dev_priv->regs + (reg))
#define I915_WRITE16(reg, val) writel(val, dev_priv->regs + (reg))
#define I915_READ8(reg) readb(dev_priv->regs + (reg))
@@ -1110,6 +1211,11 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove
#define POSTING_READ(reg) (void)I915_READ(reg)
#define POSTING_READ16(reg) (void)I915_READ16(reg)
+#define I915_DEBUG_ENABLE_IO() (dev_priv->debug_flags |= I915_DEBUG_READ | \
+ I915_DEBUG_WRITE)
+#define I915_DEBUG_DISABLE_IO() (dev_priv->debug_flags &= ~(I915_DEBUG_READ | \
+ I915_DEBUG_WRITE))
+
#define I915_VERBOSE 0
#define BEGIN_LP_RING(n) do { \
@@ -1166,8 +1272,6 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove
#define IS_I915GM(dev) ((dev)->pci_device == 0x2592)
#define IS_I945G(dev) ((dev)->pci_device == 0x2772)
#define IS_I945GM(dev) (INTEL_INFO(dev)->is_i945gm)
-#define IS_I965G(dev) (INTEL_INFO(dev)->is_i965g)
-#define IS_I965GM(dev) (INTEL_INFO(dev)->is_i965gm)
#define IS_BROADWATER(dev) (INTEL_INFO(dev)->is_broadwater)
#define IS_CRESTLINE(dev) (INTEL_INFO(dev)->is_crestline)
#define IS_GM45(dev) ((dev)->pci_device == 0x2A42)
@@ -1178,8 +1282,6 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove
#define IS_G33(dev) (INTEL_INFO(dev)->is_g33)
#define IS_IRONLAKE_D(dev) ((dev)->pci_device == 0x0042)
#define IS_IRONLAKE_M(dev) ((dev)->pci_device == 0x0046)
-#define IS_IRONLAKE(dev) (INTEL_INFO(dev)->is_ironlake)
-#define IS_I9XX(dev) (INTEL_INFO(dev)->is_i9xx)
#define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile)
#define IS_GEN2(dev) (INTEL_INFO(dev)->gen == 2)
@@ -1188,33 +1290,34 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove
#define IS_GEN5(dev) (INTEL_INFO(dev)->gen == 5)
#define IS_GEN6(dev) (INTEL_INFO(dev)->gen == 6)
-#define HAS_BSD(dev) (IS_IRONLAKE(dev) || IS_G4X(dev))
+#define HAS_BSD(dev) (INTEL_INFO(dev)->has_bsd_ring)
+#define HAS_BLT(dev) (INTEL_INFO(dev)->has_blt_ring)
#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
+#define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay)
+#define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical)
+
/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
* rows, which changed the alignment requirements and fence programming.
*/
-#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
+#define HAS_128_BYTE_Y_TILING(dev) (!IS_GEN2(dev) && !(IS_I915G(dev) || \
IS_I915GM(dev)))
-#define SUPPORTS_DIGITAL_OUTPUTS(dev) (IS_I9XX(dev) && !IS_PINEVIEW(dev))
-#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
-#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
+#define SUPPORTS_DIGITAL_OUTPUTS(dev) (!IS_GEN2(dev) && !IS_PINEVIEW(dev))
+#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_GEN5(dev))
+#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_GEN5(dev))
#define SUPPORTS_EDP(dev) (IS_IRONLAKE_M(dev))
-#define SUPPORTS_TV(dev) (IS_I9XX(dev) && IS_MOBILE(dev) && \
- !IS_IRONLAKE(dev) && !IS_PINEVIEW(dev) && \
- !IS_GEN6(dev))
+#define SUPPORTS_TV(dev) (INTEL_INFO(dev)->supports_tv)
#define I915_HAS_HOTPLUG(dev) (INTEL_INFO(dev)->has_hotplug)
/* dsparb controlled by hw only */
#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
-#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IRONLAKE(dev))
+#define HAS_FW_BLC(dev) (INTEL_INFO(dev)->gen > 2)
#define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr)
#define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
#define I915_HAS_RC6(dev) (INTEL_INFO(dev)->has_rc6)
-#define HAS_PCH_SPLIT(dev) (IS_IRONLAKE(dev) || \
- IS_GEN6(dev))
-#define HAS_PIPE_CONTROL(dev) (IS_IRONLAKE(dev) || IS_GEN6(dev))
+#define HAS_PCH_SPLIT(dev) (IS_GEN5(dev) || IS_GEN6(dev))
+#define HAS_PIPE_CONTROL(dev) (IS_GEN5(dev) || IS_GEN6(dev))
#define INTEL_PCH_TYPE(dev) (((struct drm_i915_private *)(dev)->dev_private)->pch_type)
#define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 90b1d6753b9d..8eb8453208b5 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -37,7 +37,9 @@
#include <linux/intel-gtt.h>
static uint32_t i915_gem_get_gtt_alignment(struct drm_gem_object *obj);
-static int i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj);
+
+static int i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj,
+ bool pipelined);
static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj);
static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj);
static int i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj,
@@ -46,7 +48,8 @@ static int i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
uint64_t offset,
uint64_t size);
static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj);
-static int i915_gem_object_wait_rendering(struct drm_gem_object *obj);
+static int i915_gem_object_wait_rendering(struct drm_gem_object *obj,
+ bool interruptible);
static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
unsigned alignment);
static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
@@ -55,9 +58,111 @@ static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *o
struct drm_file *file_priv);
static void i915_gem_free_object_tail(struct drm_gem_object *obj);
+static int
+i915_gem_object_get_pages(struct drm_gem_object *obj,
+ gfp_t gfpmask);
+
+static void
+i915_gem_object_put_pages(struct drm_gem_object *obj);
+
static LIST_HEAD(shrink_list);
static DEFINE_SPINLOCK(shrink_list_lock);
+/* some bookkeeping */
+static void i915_gem_info_add_obj(struct drm_i915_private *dev_priv,
+ size_t size)
+{
+ dev_priv->mm.object_count++;
+ dev_priv->mm.object_memory += size;
+}
+
+static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv,
+ size_t size)
+{
+ dev_priv->mm.object_count--;
+ dev_priv->mm.object_memory -= size;
+}
+
+static void i915_gem_info_add_gtt(struct drm_i915_private *dev_priv,
+ size_t size)
+{
+ dev_priv->mm.gtt_count++;
+ dev_priv->mm.gtt_memory += size;
+}
+
+static void i915_gem_info_remove_gtt(struct drm_i915_private *dev_priv,
+ size_t size)
+{
+ dev_priv->mm.gtt_count--;
+ dev_priv->mm.gtt_memory -= size;
+}
+
+static void i915_gem_info_add_pin(struct drm_i915_private *dev_priv,
+ size_t size)
+{
+ dev_priv->mm.pin_count++;
+ dev_priv->mm.pin_memory += size;
+}
+
+static void i915_gem_info_remove_pin(struct drm_i915_private *dev_priv,
+ size_t size)
+{
+ dev_priv->mm.pin_count--;
+ dev_priv->mm.pin_memory -= size;
+}
+
+int
+i915_gem_check_is_wedged(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct completion *x = &dev_priv->error_completion;
+ unsigned long flags;
+ int ret;
+
+ if (!atomic_read(&dev_priv->mm.wedged))
+ return 0;
+
+ ret = wait_for_completion_interruptible(x);
+ if (ret)
+ return ret;
+
+ /* Success, we reset the GPU! */
+ if (!atomic_read(&dev_priv->mm.wedged))
+ return 0;
+
+ /* GPU is hung, bump the completion count to account for
+ * the token we just consumed so that we never hit zero and
+ * end up waiting upon a subsequent completion event that
+ * will never happen.
+ */
+ spin_lock_irqsave(&x->wait.lock, flags);
+ x->done++;
+ spin_unlock_irqrestore(&x->wait.lock, flags);
+ return -EIO;
+}
+
+static int i915_mutex_lock_interruptible(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = i915_gem_check_is_wedged(dev);
+ if (ret)
+ return ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ if (atomic_read(&dev_priv->mm.wedged)) {
+ mutex_unlock(&dev->struct_mutex);
+ return -EAGAIN;
+ }
+
+ WARN_ON(i915_verify_lists(dev));
+ return 0;
+}
+
static inline bool
i915_gem_object_is_inactive(struct drm_i915_gem_object *obj_priv)
{
@@ -66,7 +171,8 @@ i915_gem_object_is_inactive(struct drm_i915_gem_object *obj_priv)
obj_priv->pin_count == 0;
}
-int i915_gem_do_init(struct drm_device *dev, unsigned long start,
+int i915_gem_do_init(struct drm_device *dev,
+ unsigned long start,
unsigned long end)
{
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -80,7 +186,7 @@ int i915_gem_do_init(struct drm_device *dev, unsigned long start,
drm_mm_init(&dev_priv->mm.gtt_space, start,
end - start);
- dev->gtt_total = (uint32_t) (end - start);
+ dev_priv->mm.gtt_total = end - start;
return 0;
}
@@ -103,14 +209,16 @@ int
i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_get_aperture *args = data;
if (!(dev->driver->driver_features & DRIVER_GEM))
return -ENODEV;
- args->aper_size = dev->gtt_total;
- args->aper_available_size = (args->aper_size -
- atomic_read(&dev->pin_memory));
+ mutex_lock(&dev->struct_mutex);
+ args->aper_size = dev_priv->mm.gtt_total;
+ args->aper_available_size = args->aper_size - dev_priv->mm.pin_memory;
+ mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -136,12 +244,17 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
return -ENOMEM;
ret = drm_gem_handle_create(file_priv, obj, &handle);
- /* drop reference from allocate - handle holds it now */
- drm_gem_object_unreference_unlocked(obj);
if (ret) {
+ drm_gem_object_release(obj);
+ i915_gem_info_remove_obj(dev->dev_private, obj->size);
+ kfree(obj);
return ret;
}
+ /* drop reference from allocate - handle holds it now */
+ drm_gem_object_unreference(obj);
+ trace_i915_gem_object_create(obj);
+
args->handle = handle;
return 0;
}
@@ -152,19 +265,14 @@ fast_shmem_read(struct page **pages,
char __user *data,
int length)
{
- char __iomem *vaddr;
- int unwritten;
-
- vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0);
- if (vaddr == NULL)
- return -ENOMEM;
- unwritten = __copy_to_user_inatomic(data, vaddr + page_offset, length);
- kunmap_atomic(vaddr, KM_USER0);
+ char *vaddr;
+ int ret;
- if (unwritten)
- return -EFAULT;
+ vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT]);
+ ret = __copy_to_user_inatomic(data, vaddr + page_offset, length);
+ kunmap_atomic(vaddr);
- return 0;
+ return ret;
}
static int i915_gem_object_needs_bit17_swizzle(struct drm_gem_object *obj)
@@ -258,22 +366,10 @@ i915_gem_shmem_pread_fast(struct drm_device *dev, struct drm_gem_object *obj,
loff_t offset, page_base;
char __user *user_data;
int page_offset, page_length;
- int ret;
user_data = (char __user *) (uintptr_t) args->data_ptr;
remain = args->size;
- mutex_lock(&dev->struct_mutex);
-
- ret = i915_gem_object_get_pages(obj, 0);
- if (ret != 0)
- goto fail_unlock;
-
- ret = i915_gem_object_set_cpu_read_domain_range(obj, args->offset,
- args->size);
- if (ret != 0)
- goto fail_put_pages;
-
obj_priv = to_intel_bo(obj);
offset = args->offset;
@@ -290,23 +386,17 @@ i915_gem_shmem_pread_fast(struct drm_device *dev, struct drm_gem_object *obj,
if ((page_offset + remain) > PAGE_SIZE)
page_length = PAGE_SIZE - page_offset;
- ret = fast_shmem_read(obj_priv->pages,
- page_base, page_offset,
- user_data, page_length);
- if (ret)
- goto fail_put_pages;
+ if (fast_shmem_read(obj_priv->pages,
+ page_base, page_offset,
+ user_data, page_length))
+ return -EFAULT;
remain -= page_length;
user_data += page_length;
offset += page_length;
}
-fail_put_pages:
- i915_gem_object_put_pages(obj);
-fail_unlock:
- mutex_unlock(&dev->struct_mutex);
-
- return ret;
+ return 0;
}
static int
@@ -367,31 +457,28 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1;
- user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
+ 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, 1, 0, user_pages, NULL);
up_read(&mm->mmap_sem);
+ mutex_lock(&dev->struct_mutex);
if (pinned_pages < num_pages) {
ret = -EFAULT;
- goto fail_put_user_pages;
+ goto out;
}
- do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
-
- mutex_lock(&dev->struct_mutex);
-
- ret = i915_gem_object_get_pages_or_evict(obj);
+ ret = i915_gem_object_set_cpu_read_domain_range(obj,
+ args->offset,
+ args->size);
if (ret)
- goto fail_unlock;
+ goto out;
- ret = i915_gem_object_set_cpu_read_domain_range(obj, args->offset,
- args->size);
- if (ret != 0)
- goto fail_put_pages;
+ do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
obj_priv = to_intel_bo(obj);
offset = args->offset;
@@ -436,11 +523,7 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj,
offset += page_length;
}
-fail_put_pages:
- i915_gem_object_put_pages(obj);
-fail_unlock:
- mutex_unlock(&dev->struct_mutex);
-fail_put_user_pages:
+out:
for (i = 0; i < pinned_pages; i++) {
SetPageDirty(user_pages[i]);
page_cache_release(user_pages[i]);
@@ -462,37 +545,64 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_pread *args = data;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
- int ret;
+ int ret = 0;
+
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
- if (obj == NULL)
- return -ENOENT;
+ if (obj == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
obj_priv = to_intel_bo(obj);
/* Bounds check source. */
if (args->offset > obj->size || args->size > obj->size - args->offset) {
ret = -EINVAL;
- goto err;
+ goto out;
}
+ if (args->size == 0)
+ goto out;
+
if (!access_ok(VERIFY_WRITE,
(char __user *)(uintptr_t)args->data_ptr,
args->size)) {
ret = -EFAULT;
- goto err;
+ goto out;
}
- if (i915_gem_object_needs_bit17_swizzle(obj)) {
- ret = i915_gem_shmem_pread_slow(dev, obj, args, file_priv);
- } else {
- ret = i915_gem_shmem_pread_fast(dev, obj, args, file_priv);
- if (ret != 0)
- ret = i915_gem_shmem_pread_slow(dev, obj, args,
- file_priv);
+ ret = fault_in_pages_writeable((char __user *)(uintptr_t)args->data_ptr,
+ args->size);
+ if (ret) {
+ ret = -EFAULT;
+ goto out;
}
-err:
- drm_gem_object_unreference_unlocked(obj);
+ ret = i915_gem_object_get_pages_or_evict(obj);
+ if (ret)
+ goto out;
+
+ ret = i915_gem_object_set_cpu_read_domain_range(obj,
+ args->offset,
+ args->size);
+ if (ret)
+ goto out_put;
+
+ ret = -EFAULT;
+ if (!i915_gem_object_needs_bit17_swizzle(obj))
+ ret = i915_gem_shmem_pread_fast(dev, obj, args, file_priv);
+ if (ret == -EFAULT)
+ ret = i915_gem_shmem_pread_slow(dev, obj, args, file_priv);
+
+out_put:
+ i915_gem_object_put_pages(obj);
+out:
+ drm_gem_object_unreference(obj);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
return ret;
}
@@ -509,13 +619,11 @@ fast_user_write(struct io_mapping *mapping,
char *vaddr_atomic;
unsigned long unwritten;
- vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base, KM_USER0);
+ vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base);
unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset,
user_data, length);
- io_mapping_unmap_atomic(vaddr_atomic, KM_USER0);
- if (unwritten)
- return -EFAULT;
- return 0;
+ io_mapping_unmap_atomic(vaddr_atomic);
+ return unwritten;
}
/* Here's the write path which can sleep for
@@ -548,18 +656,14 @@ fast_shmem_write(struct page **pages,
char __user *data,
int length)
{
- char __iomem *vaddr;
- unsigned long unwritten;
+ char *vaddr;
+ int ret;
- vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0);
- if (vaddr == NULL)
- return -ENOMEM;
- unwritten = __copy_from_user_inatomic(vaddr + page_offset, data, length);
- kunmap_atomic(vaddr, KM_USER0);
+ vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT]);
+ ret = __copy_from_user_inatomic(vaddr + page_offset, data, length);
+ kunmap_atomic(vaddr);
- if (unwritten)
- return -EFAULT;
- return 0;
+ return ret;
}
/**
@@ -577,22 +681,10 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
loff_t offset, page_base;
char __user *user_data;
int page_offset, page_length;
- int ret;
user_data = (char __user *) (uintptr_t) args->data_ptr;
remain = args->size;
-
- mutex_lock(&dev->struct_mutex);
- ret = i915_gem_object_pin(obj, 0);
- if (ret) {
- mutex_unlock(&dev->struct_mutex);
- return ret;
- }
- ret = i915_gem_object_set_to_gtt_domain(obj, 1);
- if (ret)
- goto fail;
-
obj_priv = to_intel_bo(obj);
offset = obj_priv->gtt_offset + args->offset;
@@ -609,26 +701,21 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
if ((page_offset + remain) > PAGE_SIZE)
page_length = PAGE_SIZE - page_offset;
- ret = fast_user_write (dev_priv->mm.gtt_mapping, page_base,
- page_offset, user_data, page_length);
-
/* If we get a fault while copying data, then (presumably) our
* source page isn't available. Return the error and we'll
* retry in the slow path.
*/
- if (ret)
- goto fail;
+ if (fast_user_write(dev_priv->mm.gtt_mapping, page_base,
+ page_offset, user_data, page_length))
+
+ return -EFAULT;
remain -= page_length;
user_data += page_length;
offset += page_length;
}
-fail:
- i915_gem_object_unpin(obj);
- mutex_unlock(&dev->struct_mutex);
-
- return ret;
+ return 0;
}
/**
@@ -665,27 +752,24 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1;
- user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
+ 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_unpin_pages;
}
- mutex_lock(&dev->struct_mutex);
- ret = i915_gem_object_pin(obj, 0);
- if (ret)
- goto out_unlock;
-
ret = i915_gem_object_set_to_gtt_domain(obj, 1);
if (ret)
- goto out_unpin_object;
+ goto out_unpin_pages;
obj_priv = to_intel_bo(obj);
offset = obj_priv->gtt_offset + args->offset;
@@ -721,10 +805,6 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
data_ptr += page_length;
}
-out_unpin_object:
- i915_gem_object_unpin(obj);
-out_unlock:
- mutex_unlock(&dev->struct_mutex);
out_unpin_pages:
for (i = 0; i < pinned_pages; i++)
page_cache_release(user_pages[i]);
@@ -747,21 +827,10 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
loff_t offset, page_base;
char __user *user_data;
int page_offset, page_length;
- int ret;
user_data = (char __user *) (uintptr_t) args->data_ptr;
remain = args->size;
- mutex_lock(&dev->struct_mutex);
-
- ret = i915_gem_object_get_pages(obj, 0);
- if (ret != 0)
- goto fail_unlock;
-
- ret = i915_gem_object_set_to_cpu_domain(obj, 1);
- if (ret != 0)
- goto fail_put_pages;
-
obj_priv = to_intel_bo(obj);
offset = args->offset;
obj_priv->dirty = 1;
@@ -779,23 +848,17 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
if ((page_offset + remain) > PAGE_SIZE)
page_length = PAGE_SIZE - page_offset;
- ret = fast_shmem_write(obj_priv->pages,
+ if (fast_shmem_write(obj_priv->pages,
page_base, page_offset,
- user_data, page_length);
- if (ret)
- goto fail_put_pages;
+ user_data, page_length))
+ return -EFAULT;
remain -= page_length;
user_data += page_length;
offset += page_length;
}
-fail_put_pages:
- i915_gem_object_put_pages(obj);
-fail_unlock:
- mutex_unlock(&dev->struct_mutex);
-
- return ret;
+ return 0;
}
/**
@@ -833,30 +896,26 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
num_pages = last_data_page - first_data_page + 1;
- user_pages = drm_calloc_large(num_pages, sizeof(struct page *));
+ 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 fail_put_user_pages;
+ goto out;
}
- do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
-
- mutex_lock(&dev->struct_mutex);
-
- ret = i915_gem_object_get_pages_or_evict(obj);
+ ret = i915_gem_object_set_to_cpu_domain(obj, 1);
if (ret)
- goto fail_unlock;
+ goto out;
- ret = i915_gem_object_set_to_cpu_domain(obj, 1);
- if (ret != 0)
- goto fail_put_pages;
+ do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
obj_priv = to_intel_bo(obj);
offset = args->offset;
@@ -902,11 +961,7 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
offset += page_length;
}
-fail_put_pages:
- i915_gem_object_put_pages(obj);
-fail_unlock:
- mutex_unlock(&dev->struct_mutex);
-fail_put_user_pages:
+out:
for (i = 0; i < pinned_pages; i++)
page_cache_release(user_pages[i]);
drm_free_large(user_pages);
@@ -921,29 +976,46 @@ fail_put_user_pages:
*/
int
i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ struct drm_file *file)
{
struct drm_i915_gem_pwrite *args = data;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
int ret = 0;
- obj = drm_gem_object_lookup(dev, file_priv, args->handle);
- if (obj == NULL)
- return -ENOENT;
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+
+ obj = drm_gem_object_lookup(dev, file, args->handle);
+ if (obj == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
obj_priv = to_intel_bo(obj);
+
/* Bounds check destination. */
if (args->offset > obj->size || args->size > obj->size - args->offset) {
ret = -EINVAL;
- goto err;
+ goto out;
}
+ if (args->size == 0)
+ goto out;
+
if (!access_ok(VERIFY_READ,
(char __user *)(uintptr_t)args->data_ptr,
args->size)) {
ret = -EFAULT;
- goto err;
+ goto out;
+ }
+
+ ret = fault_in_pages_readable((char __user *)(uintptr_t)args->data_ptr,
+ args->size);
+ if (ret) {
+ ret = -EFAULT;
+ goto out;
}
/* We can only do the GTT pwrite on untiled buffers, as otherwise
@@ -953,32 +1025,47 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
* perspective, requiring manual detiling by the client.
*/
if (obj_priv->phys_obj)
- ret = i915_gem_phys_pwrite(dev, obj, args, file_priv);
+ ret = i915_gem_phys_pwrite(dev, obj, args, file);
else if (obj_priv->tiling_mode == I915_TILING_NONE &&
- dev->gtt_total != 0 &&
+ obj_priv->gtt_space &&
obj->write_domain != I915_GEM_DOMAIN_CPU) {
- ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file_priv);
- if (ret == -EFAULT) {
- ret = i915_gem_gtt_pwrite_slow(dev, obj, args,
- file_priv);
- }
- } else if (i915_gem_object_needs_bit17_swizzle(obj)) {
- ret = i915_gem_shmem_pwrite_slow(dev, obj, args, file_priv);
+ ret = i915_gem_object_pin(obj, 0);
+ if (ret)
+ goto out;
+
+ ret = i915_gem_object_set_to_gtt_domain(obj, 1);
+ if (ret)
+ goto out_unpin;
+
+ ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file);
+ if (ret == -EFAULT)
+ ret = i915_gem_gtt_pwrite_slow(dev, obj, args, file);
+
+out_unpin:
+ i915_gem_object_unpin(obj);
} else {
- ret = i915_gem_shmem_pwrite_fast(dev, obj, args, file_priv);
- if (ret == -EFAULT) {
- ret = i915_gem_shmem_pwrite_slow(dev, obj, args,
- file_priv);
- }
- }
+ ret = i915_gem_object_get_pages_or_evict(obj);
+ if (ret)
+ goto out;
-#if WATCH_PWRITE
- if (ret)
- DRM_INFO("pwrite failed %d\n", ret);
-#endif
+ ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+ if (ret)
+ goto out_put;
-err:
- drm_gem_object_unreference_unlocked(obj);
+ 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_put:
+ i915_gem_object_put_pages(obj);
+ }
+
+out:
+ drm_gem_object_unreference(obj);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
return ret;
}
@@ -1014,19 +1101,19 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
if (write_domain != 0 && read_domains != write_domain)
return -EINVAL;
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
- if (obj == NULL)
- return -ENOENT;
+ if (obj == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
obj_priv = to_intel_bo(obj);
- mutex_lock(&dev->struct_mutex);
-
intel_mark_busy(dev, obj);
-#if WATCH_BUF
- DRM_INFO("set_domain_ioctl %p(%zd), %08x %08x\n",
- obj, obj->size, read_domains, write_domain);
-#endif
if (read_domains & I915_GEM_DOMAIN_GTT) {
ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0);
@@ -1050,12 +1137,12 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0);
}
-
/* Maintain LRU order of "inactive" objects */
if (ret == 0 && i915_gem_object_is_inactive(obj_priv))
- list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+ list_move_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
drm_gem_object_unreference(obj);
+unlock:
mutex_unlock(&dev->struct_mutex);
return ret;
}
@@ -1069,30 +1156,27 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
{
struct drm_i915_gem_sw_finish *args = data;
struct drm_gem_object *obj;
- struct drm_i915_gem_object *obj_priv;
int ret = 0;
if (!(dev->driver->driver_features & DRIVER_GEM))
return -ENODEV;
- mutex_lock(&dev->struct_mutex);
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
- mutex_unlock(&dev->struct_mutex);
- return -ENOENT;
+ ret = -ENOENT;
+ goto unlock;
}
-#if WATCH_BUF
- DRM_INFO("%s: sw_finish %d (%p %zd)\n",
- __func__, args->handle, obj, obj->size);
-#endif
- obj_priv = to_intel_bo(obj);
-
/* Pinned buffers may be scanout, so flush the cache */
- if (obj_priv->pin_count)
+ if (to_intel_bo(obj)->pin_count)
i915_gem_object_flush_cpu_write_domain(obj);
drm_gem_object_unreference(obj);
+unlock:
mutex_unlock(&dev->struct_mutex);
return ret;
}
@@ -1181,13 +1265,13 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
/* Need a new fence register? */
if (obj_priv->tiling_mode != I915_TILING_NONE) {
- ret = i915_gem_object_get_fence_reg(obj);
+ ret = i915_gem_object_get_fence_reg(obj, true);
if (ret)
goto unlock;
}
if (i915_gem_object_is_inactive(obj_priv))
- list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+ list_move_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) +
page_offset;
@@ -1246,7 +1330,7 @@ i915_gem_create_mmap_offset(struct drm_gem_object *obj)
obj->size / PAGE_SIZE, 0, 0);
if (!list->file_offset_node) {
DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
- ret = -ENOMEM;
+ ret = -ENOSPC;
goto out_free_list;
}
@@ -1258,9 +1342,9 @@ i915_gem_create_mmap_offset(struct drm_gem_object *obj)
}
list->hash.key = list->file_offset_node->start;
- if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) {
+ ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
+ if (ret) {
DRM_ERROR("failed to add to map hash\n");
- ret = -ENOMEM;
goto out_free_mm;
}
@@ -1345,14 +1429,14 @@ i915_gem_get_gtt_alignment(struct drm_gem_object *obj)
* Minimum alignment is 4k (GTT page size), but might be greater
* if a fence register is needed for the object.
*/
- if (IS_I965G(dev) || obj_priv->tiling_mode == I915_TILING_NONE)
+ if (INTEL_INFO(dev)->gen >= 4 || obj_priv->tiling_mode == I915_TILING_NONE)
return 4096;
/*
* Previous chips need to be aligned to the size of the smallest
* fence register that can contain the object.
*/
- if (IS_I9XX(dev))
+ if (INTEL_INFO(dev)->gen == 3)
start = 1024*1024;
else
start = 512*1024;
@@ -1390,29 +1474,27 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
if (!(dev->driver->driver_features & DRIVER_GEM))
return -ENODEV;
- obj = drm_gem_object_lookup(dev, file_priv, args->handle);
- if (obj == NULL)
- return -ENOENT;
-
- mutex_lock(&dev->struct_mutex);
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
obj_priv = to_intel_bo(obj);
if (obj_priv->madv != I915_MADV_WILLNEED) {
DRM_ERROR("Attempting to mmap a purgeable buffer\n");
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
-
if (!obj_priv->mmap_offset) {
ret = i915_gem_create_mmap_offset(obj);
- if (ret) {
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return ret;
- }
+ if (ret)
+ goto out;
}
args->offset = obj_priv->mmap_offset;
@@ -1423,20 +1505,18 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
*/
if (!obj_priv->agp_mem) {
ret = i915_gem_object_bind_to_gtt(obj, 0);
- if (ret) {
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return ret;
- }
+ if (ret)
+ goto out;
}
+out:
drm_gem_object_unreference(obj);
+unlock:
mutex_unlock(&dev->struct_mutex);
-
- return 0;
+ return ret;
}
-void
+static void
i915_gem_object_put_pages(struct drm_gem_object *obj)
{
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
@@ -1470,13 +1550,25 @@ i915_gem_object_put_pages(struct drm_gem_object *obj)
obj_priv->pages = NULL;
}
+static uint32_t
+i915_gem_next_request_seqno(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ ring->outstanding_lazy_request = true;
+ return dev_priv->next_seqno;
+}
+
static void
-i915_gem_object_move_to_active(struct drm_gem_object *obj, uint32_t seqno,
+i915_gem_object_move_to_active(struct drm_gem_object *obj,
struct intel_ring_buffer *ring)
{
struct drm_device *dev = obj->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
+ uint32_t seqno = i915_gem_next_request_seqno(dev, ring);
+
BUG_ON(ring == NULL);
obj_priv->ring = ring;
@@ -1485,10 +1577,10 @@ i915_gem_object_move_to_active(struct drm_gem_object *obj, uint32_t seqno,
drm_gem_object_reference(obj);
obj_priv->active = 1;
}
+
/* Move from whatever list we were on to the tail of execution. */
- spin_lock(&dev_priv->mm.active_list_lock);
- list_move_tail(&obj_priv->list, &ring->active_list);
- spin_unlock(&dev_priv->mm.active_list_lock);
+ list_move_tail(&obj_priv->mm_list, &dev_priv->mm.active_list);
+ list_move_tail(&obj_priv->ring_list, &ring->active_list);
obj_priv->last_rendering_seqno = seqno;
}
@@ -1500,7 +1592,8 @@ i915_gem_object_move_to_flushing(struct drm_gem_object *obj)
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
BUG_ON(!obj_priv->active);
- list_move_tail(&obj_priv->list, &dev_priv->mm.flushing_list);
+ list_move_tail(&obj_priv->mm_list, &dev_priv->mm.flushing_list);
+ list_del_init(&obj_priv->ring_list);
obj_priv->last_rendering_seqno = 0;
}
@@ -1538,11 +1631,11 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
- i915_verify_inactive(dev, __FILE__, __LINE__);
if (obj_priv->pin_count != 0)
- list_del_init(&obj_priv->list);
+ list_move_tail(&obj_priv->mm_list, &dev_priv->mm.pinned_list);
else
- list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+ list_move_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
+ list_del_init(&obj_priv->ring_list);
BUG_ON(!list_empty(&obj_priv->gpu_write_list));
@@ -1552,30 +1645,28 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
obj_priv->active = 0;
drm_gem_object_unreference(obj);
}
- i915_verify_inactive(dev, __FILE__, __LINE__);
+ WARN_ON(i915_verify_lists(dev));
}
static void
i915_gem_process_flushing_list(struct drm_device *dev,
- uint32_t flush_domains, uint32_t seqno,
+ uint32_t flush_domains,
struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv, *next;
list_for_each_entry_safe(obj_priv, next,
- &dev_priv->mm.gpu_write_list,
+ &ring->gpu_write_list,
gpu_write_list) {
struct drm_gem_object *obj = &obj_priv->base;
- if ((obj->write_domain & flush_domains) ==
- obj->write_domain &&
- obj_priv->ring->ring_flag == ring->ring_flag) {
+ if (obj->write_domain & flush_domains) {
uint32_t old_write_domain = obj->write_domain;
obj->write_domain = 0;
list_del_init(&obj_priv->gpu_write_list);
- i915_gem_object_move_to_active(obj, seqno, ring);
+ i915_gem_object_move_to_active(obj, ring);
/* update the fence lru list */
if (obj_priv->fence_reg != I915_FENCE_REG_NONE) {
@@ -1593,23 +1684,27 @@ i915_gem_process_flushing_list(struct drm_device *dev,
}
uint32_t
-i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
- uint32_t flush_domains, struct intel_ring_buffer *ring)
+i915_add_request(struct drm_device *dev,
+ struct drm_file *file,
+ struct drm_i915_gem_request *request,
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_file_private *i915_file_priv = NULL;
- struct drm_i915_gem_request *request;
+ struct drm_i915_file_private *file_priv = NULL;
uint32_t seqno;
int was_empty;
- if (file_priv != NULL)
- i915_file_priv = file_priv->driver_priv;
+ if (file != NULL)
+ file_priv = file->driver_priv;
- request = kzalloc(sizeof(*request), GFP_KERNEL);
- if (request == NULL)
- return 0;
+ if (request == NULL) {
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (request == NULL)
+ return 0;
+ }
- seqno = ring->add_request(dev, ring, file_priv, flush_domains);
+ seqno = ring->add_request(dev, ring, 0);
+ ring->outstanding_lazy_request = false;
request->seqno = seqno;
request->ring = ring;
@@ -1617,23 +1712,20 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
was_empty = list_empty(&ring->request_list);
list_add_tail(&request->list, &ring->request_list);
- if (i915_file_priv) {
+ if (file_priv) {
+ spin_lock(&file_priv->mm.lock);
+ request->file_priv = file_priv;
list_add_tail(&request->client_list,
- &i915_file_priv->mm.request_list);
- } else {
- INIT_LIST_HEAD(&request->client_list);
+ &file_priv->mm.request_list);
+ spin_unlock(&file_priv->mm.lock);
}
- /* Associate any objects on the flushing list matching the write
- * domain we're flushing with our flush.
- */
- if (flush_domains != 0)
- i915_gem_process_flushing_list(dev, flush_domains, seqno, ring);
-
if (!dev_priv->mm.suspended) {
- mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
+ mod_timer(&dev_priv->hangcheck_timer,
+ jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
if (was_empty)
- queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
+ queue_delayed_work(dev_priv->wq,
+ &dev_priv->mm.retire_work, HZ);
}
return seqno;
}
@@ -1644,91 +1736,105 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
* Ensures that all commands in the ring are finished
* before signalling the CPU
*/
-static uint32_t
+static void
i915_retire_commands(struct drm_device *dev, struct intel_ring_buffer *ring)
{
uint32_t flush_domains = 0;
/* The sampler always gets flushed on i965 (sigh) */
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
flush_domains |= I915_GEM_DOMAIN_SAMPLER;
ring->flush(dev, ring,
I915_GEM_DOMAIN_COMMAND, flush_domains);
- return flush_domains;
}
-/**
- * Moves buffers associated only with the given active seqno from the active
- * to inactive list, potentially freeing them.
- */
-static void
-i915_gem_retire_request(struct drm_device *dev,
- struct drm_i915_gem_request *request)
+static inline void
+i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_file_private *file_priv = request->file_priv;
- trace_i915_gem_request_retire(dev, request->seqno);
+ if (!file_priv)
+ return;
- /* Move any buffers on the active list that are no longer referenced
- * by the ringbuffer to the flushing/inactive lists as appropriate.
- */
- spin_lock(&dev_priv->mm.active_list_lock);
- while (!list_empty(&request->ring->active_list)) {
- struct drm_gem_object *obj;
- struct drm_i915_gem_object *obj_priv;
+ spin_lock(&file_priv->mm.lock);
+ list_del(&request->client_list);
+ request->file_priv = NULL;
+ spin_unlock(&file_priv->mm.lock);
+}
- obj_priv = list_first_entry(&request->ring->active_list,
- struct drm_i915_gem_object,
- list);
- obj = &obj_priv->base;
+static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv,
+ struct intel_ring_buffer *ring)
+{
+ while (!list_empty(&ring->request_list)) {
+ struct drm_i915_gem_request *request;
- /* If the seqno being retired doesn't match the oldest in the
- * list, then the oldest in the list must still be newer than
- * this seqno.
- */
- if (obj_priv->last_rendering_seqno != request->seqno)
- goto out;
+ request = list_first_entry(&ring->request_list,
+ struct drm_i915_gem_request,
+ list);
-#if WATCH_LRU
- DRM_INFO("%s: retire %d moves to inactive list %p\n",
- __func__, request->seqno, obj);
-#endif
+ list_del(&request->list);
+ i915_gem_request_remove_from_client(request);
+ kfree(request);
+ }
- if (obj->write_domain != 0)
- i915_gem_object_move_to_flushing(obj);
- else {
- /* Take a reference on the object so it won't be
- * freed while the spinlock is held. The list
- * protection for this spinlock is safe when breaking
- * the lock like this since the next thing we do
- * is just get the head of the list again.
- */
- drm_gem_object_reference(obj);
- i915_gem_object_move_to_inactive(obj);
- spin_unlock(&dev_priv->mm.active_list_lock);
- drm_gem_object_unreference(obj);
- spin_lock(&dev_priv->mm.active_list_lock);
- }
+ while (!list_empty(&ring->active_list)) {
+ struct drm_i915_gem_object *obj_priv;
+
+ obj_priv = list_first_entry(&ring->active_list,
+ struct drm_i915_gem_object,
+ ring_list);
+
+ obj_priv->base.write_domain = 0;
+ list_del_init(&obj_priv->gpu_write_list);
+ i915_gem_object_move_to_inactive(&obj_priv->base);
}
-out:
- spin_unlock(&dev_priv->mm.active_list_lock);
}
-/**
- * Returns true if seq1 is later than seq2.
- */
-bool
-i915_seqno_passed(uint32_t seq1, uint32_t seq2)
+void i915_gem_reset(struct drm_device *dev)
{
- return (int32_t)(seq1 - seq2) >= 0;
-}
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv;
+ int i;
-uint32_t
-i915_get_gem_seqno(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- return ring->get_gem_seqno(dev, ring);
+ i915_gem_reset_ring_lists(dev_priv, &dev_priv->render_ring);
+ i915_gem_reset_ring_lists(dev_priv, &dev_priv->bsd_ring);
+ i915_gem_reset_ring_lists(dev_priv, &dev_priv->blt_ring);
+
+ /* Remove anything from the flushing lists. The GPU cache is likely
+ * to be lost on reset along with the data, so simply move the
+ * lost bo to the inactive list.
+ */
+ while (!list_empty(&dev_priv->mm.flushing_list)) {
+ obj_priv = list_first_entry(&dev_priv->mm.flushing_list,
+ struct drm_i915_gem_object,
+ mm_list);
+
+ obj_priv->base.write_domain = 0;
+ list_del_init(&obj_priv->gpu_write_list);
+ i915_gem_object_move_to_inactive(&obj_priv->base);
+ }
+
+ /* Move everything out of the GPU domains to ensure we do any
+ * necessary invalidation upon reuse.
+ */
+ list_for_each_entry(obj_priv,
+ &dev_priv->mm.inactive_list,
+ mm_list)
+ {
+ obj_priv->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
+ }
+
+ /* The fence registers are invalidated so clear them out */
+ for (i = 0; i < 16; i++) {
+ struct drm_i915_fence_reg *reg;
+
+ reg = &dev_priv->fence_regs[i];
+ if (!reg->obj)
+ continue;
+
+ i915_gem_clear_fence_reg(reg->obj);
+ }
}
/**
@@ -1741,38 +1847,58 @@ i915_gem_retire_requests_ring(struct drm_device *dev,
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t seqno;
- if (!ring->status_page.page_addr
- || list_empty(&ring->request_list))
+ if (!ring->status_page.page_addr ||
+ list_empty(&ring->request_list))
return;
- seqno = i915_get_gem_seqno(dev, ring);
+ WARN_ON(i915_verify_lists(dev));
+ seqno = ring->get_seqno(dev, ring);
while (!list_empty(&ring->request_list)) {
struct drm_i915_gem_request *request;
- uint32_t retiring_seqno;
request = list_first_entry(&ring->request_list,
struct drm_i915_gem_request,
list);
- retiring_seqno = request->seqno;
- if (i915_seqno_passed(seqno, retiring_seqno) ||
- atomic_read(&dev_priv->mm.wedged)) {
- i915_gem_retire_request(dev, request);
+ if (!i915_seqno_passed(seqno, request->seqno))
+ break;
+
+ trace_i915_gem_request_retire(dev, request->seqno);
+
+ list_del(&request->list);
+ i915_gem_request_remove_from_client(request);
+ kfree(request);
+ }
+
+ /* Move any buffers on the active list that are no longer referenced
+ * by the ringbuffer to the flushing/inactive lists as appropriate.
+ */
+ while (!list_empty(&ring->active_list)) {
+ struct drm_gem_object *obj;
+ struct drm_i915_gem_object *obj_priv;
+
+ obj_priv = list_first_entry(&ring->active_list,
+ struct drm_i915_gem_object,
+ ring_list);
- list_del(&request->list);
- list_del(&request->client_list);
- kfree(request);
- } else
+ if (!i915_seqno_passed(seqno, obj_priv->last_rendering_seqno))
break;
+
+ obj = &obj_priv->base;
+ if (obj->write_domain != 0)
+ i915_gem_object_move_to_flushing(obj);
+ else
+ i915_gem_object_move_to_inactive(obj);
}
if (unlikely (dev_priv->trace_irq_seqno &&
i915_seqno_passed(dev_priv->trace_irq_seqno, seqno))) {
-
ring->user_irq_put(dev, ring);
dev_priv->trace_irq_seqno = 0;
}
+
+ WARN_ON(i915_verify_lists(dev));
}
void
@@ -1790,16 +1916,16 @@ i915_gem_retire_requests(struct drm_device *dev)
*/
list_for_each_entry_safe(obj_priv, tmp,
&dev_priv->mm.deferred_free_list,
- list)
+ mm_list)
i915_gem_free_object_tail(&obj_priv->base);
}
i915_gem_retire_requests_ring(dev, &dev_priv->render_ring);
- if (HAS_BSD(dev))
- i915_gem_retire_requests_ring(dev, &dev_priv->bsd_ring);
+ i915_gem_retire_requests_ring(dev, &dev_priv->bsd_ring);
+ i915_gem_retire_requests_ring(dev, &dev_priv->blt_ring);
}
-void
+static void
i915_gem_retire_work_handler(struct work_struct *work)
{
drm_i915_private_t *dev_priv;
@@ -1809,20 +1935,25 @@ i915_gem_retire_work_handler(struct work_struct *work)
mm.retire_work.work);
dev = dev_priv->dev;
- mutex_lock(&dev->struct_mutex);
+ /* Come back later if the device is busy... */
+ if (!mutex_trylock(&dev->struct_mutex)) {
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
+ return;
+ }
+
i915_gem_retire_requests(dev);
if (!dev_priv->mm.suspended &&
(!list_empty(&dev_priv->render_ring.request_list) ||
- (HAS_BSD(dev) &&
- !list_empty(&dev_priv->bsd_ring.request_list))))
+ !list_empty(&dev_priv->bsd_ring.request_list) ||
+ !list_empty(&dev_priv->blt_ring.request_list)))
queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
mutex_unlock(&dev->struct_mutex);
}
int
i915_do_wait_request(struct drm_device *dev, uint32_t seqno,
- int interruptible, struct intel_ring_buffer *ring)
+ bool interruptible, struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
u32 ier;
@@ -1831,9 +1962,16 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno,
BUG_ON(seqno == 0);
if (atomic_read(&dev_priv->mm.wedged))
- return -EIO;
+ return -EAGAIN;
- if (!i915_seqno_passed(ring->get_gem_seqno(dev, ring), seqno)) {
+ if (ring->outstanding_lazy_request) {
+ seqno = i915_add_request(dev, NULL, NULL, ring);
+ if (seqno == 0)
+ return -ENOMEM;
+ }
+ BUG_ON(seqno == dev_priv->next_seqno);
+
+ if (!i915_seqno_passed(ring->get_seqno(dev, ring), seqno)) {
if (HAS_PCH_SPLIT(dev))
ier = I915_READ(DEIER) | I915_READ(GTIER);
else
@@ -1852,12 +1990,12 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno,
if (interruptible)
ret = wait_event_interruptible(ring->irq_queue,
i915_seqno_passed(
- ring->get_gem_seqno(dev, ring), seqno)
+ ring->get_seqno(dev, ring), seqno)
|| atomic_read(&dev_priv->mm.wedged));
else
wait_event(ring->irq_queue,
i915_seqno_passed(
- ring->get_gem_seqno(dev, ring), seqno)
+ ring->get_seqno(dev, ring), seqno)
|| atomic_read(&dev_priv->mm.wedged));
ring->user_irq_put(dev, ring);
@@ -1866,11 +2004,12 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno,
trace_i915_gem_request_wait_end(dev, seqno);
}
if (atomic_read(&dev_priv->mm.wedged))
- ret = -EIO;
+ ret = -EAGAIN;
if (ret && ret != -ERESTARTSYS)
- DRM_ERROR("%s returns %d (awaiting %d at %d)\n",
- __func__, ret, seqno, ring->get_gem_seqno(dev, ring));
+ DRM_ERROR("%s returns %d (awaiting %d at %d, next %d)\n",
+ __func__, ret, seqno, ring->get_seqno(dev, 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
@@ -1889,27 +2028,48 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno,
*/
static int
i915_wait_request(struct drm_device *dev, uint32_t seqno,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
return i915_do_wait_request(dev, seqno, 1, ring);
}
static void
+i915_gem_flush_ring(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct intel_ring_buffer *ring,
+ uint32_t invalidate_domains,
+ uint32_t flush_domains)
+{
+ ring->flush(dev, ring, invalidate_domains, flush_domains);
+ i915_gem_process_flushing_list(dev, flush_domains, ring);
+}
+
+static void
i915_gem_flush(struct drm_device *dev,
+ struct drm_file *file_priv,
uint32_t invalidate_domains,
- uint32_t flush_domains)
+ uint32_t flush_domains,
+ uint32_t flush_rings)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+
if (flush_domains & I915_GEM_DOMAIN_CPU)
drm_agp_chipset_flush(dev);
- dev_priv->render_ring.flush(dev, &dev_priv->render_ring,
- invalidate_domains,
- flush_domains);
-
- if (HAS_BSD(dev))
- dev_priv->bsd_ring.flush(dev, &dev_priv->bsd_ring,
- invalidate_domains,
- flush_domains);
+
+ if ((flush_domains | invalidate_domains) & I915_GEM_GPU_DOMAINS) {
+ if (flush_rings & RING_RENDER)
+ i915_gem_flush_ring(dev, file_priv,
+ &dev_priv->render_ring,
+ invalidate_domains, flush_domains);
+ if (flush_rings & RING_BSD)
+ i915_gem_flush_ring(dev, file_priv,
+ &dev_priv->bsd_ring,
+ invalidate_domains, flush_domains);
+ if (flush_rings & RING_BLT)
+ i915_gem_flush_ring(dev, file_priv,
+ &dev_priv->blt_ring,
+ invalidate_domains, flush_domains);
+ }
}
/**
@@ -1917,7 +2077,8 @@ i915_gem_flush(struct drm_device *dev,
* safe to unbind from the GTT or access from the CPU.
*/
static int
-i915_gem_object_wait_rendering(struct drm_gem_object *obj)
+i915_gem_object_wait_rendering(struct drm_gem_object *obj,
+ bool interruptible)
{
struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
@@ -1932,13 +2093,11 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj)
* it.
*/
if (obj_priv->active) {
-#if WATCH_BUF
- DRM_INFO("%s: object %p wait for seqno %08x\n",
- __func__, obj, obj_priv->last_rendering_seqno);
-#endif
- ret = i915_wait_request(dev,
- obj_priv->last_rendering_seqno, obj_priv->ring);
- if (ret != 0)
+ ret = i915_do_wait_request(dev,
+ obj_priv->last_rendering_seqno,
+ interruptible,
+ obj_priv->ring);
+ if (ret)
return ret;
}
@@ -1952,14 +2111,10 @@ int
i915_gem_object_unbind(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
int ret = 0;
-#if WATCH_BUF
- DRM_INFO("%s:%d %p\n", __func__, __LINE__, obj);
- DRM_INFO("gtt_space %p\n", obj_priv->gtt_space);
-#endif
if (obj_priv->gtt_space == NULL)
return 0;
@@ -1984,33 +2139,27 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
* should be safe and we need to cleanup or else we might
* cause memory corruption through use-after-free.
*/
+ if (ret) {
+ i915_gem_clflush_object(obj);
+ obj->read_domains = obj->write_domain = I915_GEM_DOMAIN_CPU;
+ }
/* release the fence reg _after_ flushing */
if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
i915_gem_clear_fence_reg(obj);
- if (obj_priv->agp_mem != NULL) {
- drm_unbind_agp(obj_priv->agp_mem);
- drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE);
- obj_priv->agp_mem = NULL;
- }
+ drm_unbind_agp(obj_priv->agp_mem);
+ drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE);
i915_gem_object_put_pages(obj);
BUG_ON(obj_priv->pages_refcount);
- if (obj_priv->gtt_space) {
- atomic_dec(&dev->gtt_count);
- atomic_sub(obj->size, &dev->gtt_memory);
-
- drm_mm_put_block(obj_priv->gtt_space);
- obj_priv->gtt_space = NULL;
- }
+ i915_gem_info_remove_gtt(dev_priv, obj->size);
+ list_del_init(&obj_priv->mm_list);
- /* Remove ourselves from the LRU list if present. */
- spin_lock(&dev_priv->mm.active_list_lock);
- if (!list_empty(&obj_priv->list))
- list_del_init(&obj_priv->list);
- spin_unlock(&dev_priv->mm.active_list_lock);
+ drm_mm_put_block(obj_priv->gtt_space);
+ obj_priv->gtt_space = NULL;
+ obj_priv->gtt_offset = 0;
if (i915_gem_object_is_purgeable(obj_priv))
i915_gem_object_truncate(obj);
@@ -2020,48 +2169,50 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
return ret;
}
+static int i915_ring_idle(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
+{
+ if (list_empty(&ring->gpu_write_list))
+ return 0;
+
+ i915_gem_flush_ring(dev, NULL, ring,
+ I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+ return i915_wait_request(dev,
+ i915_gem_next_request_seqno(dev, ring),
+ ring);
+}
+
int
i915_gpu_idle(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
bool lists_empty;
- uint32_t seqno1, seqno2;
int ret;
- spin_lock(&dev_priv->mm.active_list_lock);
lists_empty = (list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->render_ring.active_list) &&
- (!HAS_BSD(dev) ||
- list_empty(&dev_priv->bsd_ring.active_list)));
- spin_unlock(&dev_priv->mm.active_list_lock);
-
+ list_empty(&dev_priv->bsd_ring.active_list) &&
+ list_empty(&dev_priv->blt_ring.active_list));
if (lists_empty)
return 0;
/* Flush everything onto the inactive list. */
- i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
- seqno1 = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS,
- &dev_priv->render_ring);
- if (seqno1 == 0)
- return -ENOMEM;
- ret = i915_wait_request(dev, seqno1, &dev_priv->render_ring);
-
- if (HAS_BSD(dev)) {
- seqno2 = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS,
- &dev_priv->bsd_ring);
- if (seqno2 == 0)
- return -ENOMEM;
+ ret = i915_ring_idle(dev, &dev_priv->render_ring);
+ if (ret)
+ return ret;
- ret = i915_wait_request(dev, seqno2, &dev_priv->bsd_ring);
- if (ret)
- return ret;
- }
+ ret = i915_ring_idle(dev, &dev_priv->bsd_ring);
+ if (ret)
+ return ret;
+ ret = i915_ring_idle(dev, &dev_priv->blt_ring);
+ if (ret)
+ return ret;
- return ret;
+ return 0;
}
-int
+static int
i915_gem_object_get_pages(struct drm_gem_object *obj,
gfp_t gfpmask)
{
@@ -2241,7 +2392,8 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
}
-static int i915_find_fence_reg(struct drm_device *dev)
+static int i915_find_fence_reg(struct drm_device *dev,
+ bool interruptible)
{
struct drm_i915_fence_reg *reg = NULL;
struct drm_i915_gem_object *obj_priv = NULL;
@@ -2286,7 +2438,7 @@ static int i915_find_fence_reg(struct drm_device *dev)
* private reference to obj like the other callers of put_fence_reg
* (set_tiling ioctl) do. */
drm_gem_object_reference(obj);
- ret = i915_gem_object_put_fence_reg(obj);
+ ret = i915_gem_object_put_fence_reg(obj, interruptible);
drm_gem_object_unreference(obj);
if (ret != 0)
return ret;
@@ -2308,7 +2460,8 @@ static int i915_find_fence_reg(struct drm_device *dev)
* and tiling format.
*/
int
-i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
+i915_gem_object_get_fence_reg(struct drm_gem_object *obj,
+ bool interruptible)
{
struct drm_device *dev = obj->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2343,7 +2496,7 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
break;
}
- ret = i915_find_fence_reg(dev);
+ ret = i915_find_fence_reg(dev, interruptible);
if (ret < 0)
return ret;
@@ -2421,15 +2574,19 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj)
* i915_gem_object_put_fence_reg - waits on outstanding fenced access
* to the buffer to finish, and then resets the fence register.
* @obj: tiled object holding a fence register.
+ * @bool: whether the wait upon the fence is interruptible
*
* Zeroes out the fence register itself and clears out the associated
* data structures in dev_priv and obj_priv.
*/
int
-i915_gem_object_put_fence_reg(struct drm_gem_object *obj)
+i915_gem_object_put_fence_reg(struct drm_gem_object *obj,
+ bool interruptible)
{
struct drm_device *dev = obj->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
+ struct drm_i915_fence_reg *reg;
if (obj_priv->fence_reg == I915_FENCE_REG_NONE)
return 0;
@@ -2444,20 +2601,23 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj)
* therefore we must wait for any outstanding access to complete
* before clearing the fence.
*/
- if (!IS_I965G(dev)) {
+ reg = &dev_priv->fence_regs[obj_priv->fence_reg];
+ if (reg->gpu) {
int ret;
- ret = i915_gem_object_flush_gpu_write_domain(obj);
- if (ret != 0)
+ ret = i915_gem_object_flush_gpu_write_domain(obj, true);
+ if (ret)
return ret;
- ret = i915_gem_object_wait_rendering(obj);
- if (ret != 0)
+ ret = i915_gem_object_wait_rendering(obj, interruptible);
+ if (ret)
return ret;
+
+ reg->gpu = false;
}
i915_gem_object_flush_gtt_write_domain(obj);
- i915_gem_clear_fence_reg (obj);
+ i915_gem_clear_fence_reg(obj);
return 0;
}
@@ -2490,7 +2650,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
/* If the object is bigger than the entire aperture, reject it early
* before evicting everything in a vain attempt to find space.
*/
- if (obj->size > dev->gtt_total) {
+ if (obj->size > dev_priv->mm.gtt_total) {
DRM_ERROR("Attempting to bind an object larger than the aperture\n");
return -E2BIG;
}
@@ -2498,19 +2658,13 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
search_free:
free_space = drm_mm_search_free(&dev_priv->mm.gtt_space,
obj->size, alignment, 0);
- if (free_space != NULL) {
+ if (free_space != NULL)
obj_priv->gtt_space = drm_mm_get_block(free_space, obj->size,
alignment);
- if (obj_priv->gtt_space != NULL)
- obj_priv->gtt_offset = obj_priv->gtt_space->start;
- }
if (obj_priv->gtt_space == NULL) {
/* If the gtt is empty and we're still having trouble
* fitting our object in, we're out of memory.
*/
-#if WATCH_LRU
- DRM_INFO("%s: GTT full, evicting something\n", __func__);
-#endif
ret = i915_gem_evict_something(dev, obj->size, alignment);
if (ret)
return ret;
@@ -2518,10 +2672,6 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
goto search_free;
}
-#if WATCH_BUF
- DRM_INFO("Binding object of size %zd at 0x%08x\n",
- obj->size, obj_priv->gtt_offset);
-#endif
ret = i915_gem_object_get_pages(obj, gfpmask);
if (ret) {
drm_mm_put_block(obj_priv->gtt_space);
@@ -2553,7 +2703,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
obj_priv->agp_mem = drm_agp_bind_pages(dev,
obj_priv->pages,
obj->size >> PAGE_SHIFT,
- obj_priv->gtt_offset,
+ obj_priv->gtt_space->start,
obj_priv->agp_type);
if (obj_priv->agp_mem == NULL) {
i915_gem_object_put_pages(obj);
@@ -2566,11 +2716,10 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
goto search_free;
}
- atomic_inc(&dev->gtt_count);
- atomic_add(obj->size, &dev->gtt_memory);
/* keep track of bounds object by adding it to the inactive list */
- list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+ list_add_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
+ i915_gem_info_add_gtt(dev_priv, obj->size);
/* Assert that the object is not currently in any GPU domain. As it
* wasn't in the GTT, there shouldn't be any way it could have been in
@@ -2579,6 +2728,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS);
BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS);
+ obj_priv->gtt_offset = obj_priv->gtt_space->start;
trace_i915_gem_object_bind(obj, obj_priv->gtt_offset);
return 0;
@@ -2603,25 +2753,30 @@ i915_gem_clflush_object(struct drm_gem_object *obj)
/** Flushes any GPU write domain for the object if it's dirty. */
static int
-i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
+i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj,
+ bool pipelined)
{
struct drm_device *dev = obj->dev;
uint32_t old_write_domain;
- struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0)
return 0;
/* Queue the GPU write cache flushing we need. */
old_write_domain = obj->write_domain;
- i915_gem_flush(dev, 0, obj->write_domain);
- if (i915_add_request(dev, NULL, obj->write_domain, obj_priv->ring) == 0)
- return -ENOMEM;
+ i915_gem_flush_ring(dev, NULL,
+ to_intel_bo(obj)->ring,
+ 0, obj->write_domain);
+ BUG_ON(obj->write_domain);
trace_i915_gem_object_change_domain(obj,
obj->read_domains,
old_write_domain);
- return 0;
+
+ if (pipelined)
+ return 0;
+
+ return i915_gem_object_wait_rendering(obj, true);
}
/** Flushes the GTT write domain for the object if it's dirty. */
@@ -2665,26 +2820,6 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj)
old_write_domain);
}
-int
-i915_gem_object_flush_write_domain(struct drm_gem_object *obj)
-{
- int ret = 0;
-
- switch (obj->write_domain) {
- case I915_GEM_DOMAIN_GTT:
- i915_gem_object_flush_gtt_write_domain(obj);
- break;
- case I915_GEM_DOMAIN_CPU:
- i915_gem_object_flush_cpu_write_domain(obj);
- break;
- default:
- ret = i915_gem_object_flush_gpu_write_domain(obj);
- break;
- }
-
- return ret;
-}
-
/**
* Moves a single object to the GTT read, and possibly write domain.
*
@@ -2702,32 +2837,28 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
if (obj_priv->gtt_space == NULL)
return -EINVAL;
- ret = i915_gem_object_flush_gpu_write_domain(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj, false);
if (ret != 0)
return ret;
- /* Wait on any GPU rendering and flushing to occur. */
- ret = i915_gem_object_wait_rendering(obj);
- if (ret != 0)
- return ret;
+ i915_gem_object_flush_cpu_write_domain(obj);
+
+ if (write) {
+ ret = i915_gem_object_wait_rendering(obj, true);
+ if (ret)
+ return ret;
+ }
old_write_domain = obj->write_domain;
old_read_domains = obj->read_domains;
- /* If we're writing through the GTT domain, then CPU and GPU caches
- * will need to be invalidated at next use.
- */
- if (write)
- obj->read_domains &= I915_GEM_DOMAIN_GTT;
-
- i915_gem_object_flush_cpu_write_domain(obj);
-
/* It should now be out of any other write domains, and we can update
* the domain values for our changes.
*/
BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
obj->read_domains |= I915_GEM_DOMAIN_GTT;
if (write) {
+ obj->read_domains = I915_GEM_DOMAIN_GTT;
obj->write_domain = I915_GEM_DOMAIN_GTT;
obj_priv->dirty = 1;
}
@@ -2744,51 +2875,36 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
* wait, as in modesetting process we're not supposed to be interrupted.
*/
int
-i915_gem_object_set_to_display_plane(struct drm_gem_object *obj)
+i915_gem_object_set_to_display_plane(struct drm_gem_object *obj,
+ bool pipelined)
{
- struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
- uint32_t old_write_domain, old_read_domains;
+ uint32_t old_read_domains;
int ret;
/* Not valid to be called on unbound objects. */
if (obj_priv->gtt_space == NULL)
return -EINVAL;
- ret = i915_gem_object_flush_gpu_write_domain(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj, true);
if (ret)
return ret;
- /* Wait on any GPU rendering and flushing to occur. */
- if (obj_priv->active) {
-#if WATCH_BUF
- DRM_INFO("%s: object %p wait for seqno %08x\n",
- __func__, obj, obj_priv->last_rendering_seqno);
-#endif
- ret = i915_do_wait_request(dev,
- obj_priv->last_rendering_seqno,
- 0,
- obj_priv->ring);
- if (ret != 0)
+ /* Currently, we are always called from an non-interruptible context. */
+ if (!pipelined) {
+ ret = i915_gem_object_wait_rendering(obj, false);
+ if (ret)
return ret;
}
i915_gem_object_flush_cpu_write_domain(obj);
- old_write_domain = obj->write_domain;
old_read_domains = obj->read_domains;
-
- /* It should now be out of any other write domains, and we can update
- * the domain values for our changes.
- */
- BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
- obj->read_domains = I915_GEM_DOMAIN_GTT;
- obj->write_domain = I915_GEM_DOMAIN_GTT;
- obj_priv->dirty = 1;
+ obj->read_domains |= I915_GEM_DOMAIN_GTT;
trace_i915_gem_object_change_domain(obj,
old_read_domains,
- old_write_domain);
+ obj->write_domain);
return 0;
}
@@ -2805,12 +2921,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
uint32_t old_write_domain, old_read_domains;
int ret;
- ret = i915_gem_object_flush_gpu_write_domain(obj);
- if (ret)
- return ret;
-
- /* Wait on any GPU rendering and flushing to occur. */
- ret = i915_gem_object_wait_rendering(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj, false);
if (ret != 0)
return ret;
@@ -2821,6 +2932,12 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
*/
i915_gem_object_set_to_full_cpu_read_domain(obj);
+ if (write) {
+ ret = i915_gem_object_wait_rendering(obj, true);
+ if (ret)
+ return ret;
+ }
+
old_write_domain = obj->write_domain;
old_read_domains = obj->read_domains;
@@ -2840,7 +2957,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
* need to be invalidated at next use.
*/
if (write) {
- obj->read_domains &= I915_GEM_DOMAIN_CPU;
+ obj->read_domains = I915_GEM_DOMAIN_CPU;
obj->write_domain = I915_GEM_DOMAIN_CPU;
}
@@ -2963,26 +3080,18 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
* drm_agp_chipset_flush
*/
static void
-i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
+i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
+ struct intel_ring_buffer *ring)
{
struct drm_device *dev = obj->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
uint32_t invalidate_domains = 0;
uint32_t flush_domains = 0;
uint32_t old_read_domains;
- BUG_ON(obj->pending_read_domains & I915_GEM_DOMAIN_CPU);
- BUG_ON(obj->pending_write_domain == I915_GEM_DOMAIN_CPU);
-
intel_mark_busy(dev, obj);
-#if WATCH_BUF
- DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n",
- __func__, obj,
- obj->read_domains, obj->pending_read_domains,
- obj->write_domain, obj->pending_write_domain);
-#endif
/*
* If the object isn't moving to a new write domain,
* let the object stay in multiple read domains
@@ -3009,13 +3118,8 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
* stale data. That is, any new read domains.
*/
invalidate_domains |= obj->pending_read_domains & ~obj->read_domains;
- if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) {
-#if WATCH_BUF
- DRM_INFO("%s: CPU domain flush %08x invalidate %08x\n",
- __func__, flush_domains, invalidate_domains);
-#endif
+ if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU)
i915_gem_clflush_object(obj);
- }
old_read_domains = obj->read_domains;
@@ -3029,21 +3133,12 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
obj->pending_write_domain = obj->write_domain;
obj->read_domains = obj->pending_read_domains;
- if (flush_domains & I915_GEM_GPU_DOMAINS) {
- if (obj_priv->ring == &dev_priv->render_ring)
- dev_priv->flush_rings |= FLUSH_RENDER_RING;
- else if (obj_priv->ring == &dev_priv->bsd_ring)
- dev_priv->flush_rings |= FLUSH_BSD_RING;
- }
-
dev->invalidate_domains |= invalidate_domains;
dev->flush_domains |= flush_domains;
-#if WATCH_BUF
- DRM_INFO("%s: read %08x write %08x invalidate %08x flush %08x\n",
- __func__,
- obj->read_domains, obj->write_domain,
- dev->invalidate_domains, dev->flush_domains);
-#endif
+ if (flush_domains & I915_GEM_GPU_DOMAINS)
+ dev_priv->mm.flush_rings |= obj_priv->ring->id;
+ if (invalidate_domains & I915_GEM_GPU_DOMAINS)
+ dev_priv->mm.flush_rings |= ring->id;
trace_i915_gem_object_change_domain(obj,
old_read_domains,
@@ -3106,12 +3201,7 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
if (offset == 0 && size == obj->size)
return i915_gem_object_set_to_cpu_domain(obj, 0);
- ret = i915_gem_object_flush_gpu_write_domain(obj);
- if (ret)
- return ret;
-
- /* Wait on any GPU rendering and flushing to occur. */
- ret = i915_gem_object_wait_rendering(obj);
+ ret = i915_gem_object_flush_gpu_write_domain(obj, false);
if (ret != 0)
return ret;
i915_gem_object_flush_gtt_write_domain(obj);
@@ -3164,66 +3254,42 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
* Pin an object to the GTT and evaluate the relocations landing in it.
*/
static int
-i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
- struct drm_file *file_priv,
- struct drm_i915_gem_exec_object2 *entry,
- struct drm_i915_gem_relocation_entry *relocs)
+i915_gem_execbuffer_relocate(struct drm_i915_gem_object *obj,
+ struct drm_file *file_priv,
+ struct drm_i915_gem_exec_object2 *entry)
{
- struct drm_device *dev = obj->dev;
+ struct drm_device *dev = obj->base.dev;
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
- int i, ret;
- void __iomem *reloc_page;
- bool need_fence;
-
- need_fence = entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
- obj_priv->tiling_mode != I915_TILING_NONE;
-
- /* Check fence reg constraints and rebind if necessary */
- if (need_fence &&
- !i915_gem_object_fence_offset_ok(obj,
- obj_priv->tiling_mode)) {
- ret = i915_gem_object_unbind(obj);
- if (ret)
- return ret;
- }
+ struct drm_i915_gem_relocation_entry __user *user_relocs;
+ struct drm_gem_object *target_obj = NULL;
+ uint32_t target_handle = 0;
+ int i, ret = 0;
- /* Choose the GTT offset for our buffer and put it there. */
- ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment);
- if (ret)
- return ret;
+ user_relocs = (void __user *)(uintptr_t)entry->relocs_ptr;
+ for (i = 0; i < entry->relocation_count; i++) {
+ struct drm_i915_gem_relocation_entry reloc;
+ uint32_t target_offset;
- /*
- * Pre-965 chips need a fence register set up in order to
- * properly handle blits to/from tiled surfaces.
- */
- if (need_fence) {
- ret = i915_gem_object_get_fence_reg(obj);
- if (ret != 0) {
- i915_gem_object_unpin(obj);
- return ret;
+ if (__copy_from_user_inatomic(&reloc,
+ user_relocs+i,
+ sizeof(reloc))) {
+ ret = -EFAULT;
+ break;
}
- }
- entry->offset = obj_priv->gtt_offset;
+ if (reloc.target_handle != target_handle) {
+ drm_gem_object_unreference(target_obj);
- /* Apply the relocations, using the GTT aperture to avoid cache
- * flushing requirements.
- */
- for (i = 0; i < entry->relocation_count; i++) {
- struct drm_i915_gem_relocation_entry *reloc= &relocs[i];
- struct drm_gem_object *target_obj;
- struct drm_i915_gem_object *target_obj_priv;
- uint32_t reloc_val, reloc_offset;
- uint32_t __iomem *reloc_entry;
-
- target_obj = drm_gem_object_lookup(obj->dev, file_priv,
- reloc->target_handle);
- if (target_obj == NULL) {
- i915_gem_object_unpin(obj);
- return -ENOENT;
+ target_obj = drm_gem_object_lookup(dev, file_priv,
+ reloc.target_handle);
+ if (target_obj == NULL) {
+ ret = -ENOENT;
+ break;
+ }
+
+ target_handle = reloc.target_handle;
}
- target_obj_priv = to_intel_bo(target_obj);
+ target_offset = to_intel_bo(target_obj)->gtt_offset;
#if WATCH_RELOC
DRM_INFO("%s: obj %p offset %08x target %d "
@@ -3231,268 +3297,266 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
"presumed %08x delta %08x\n",
__func__,
obj,
- (int) reloc->offset,
- (int) reloc->target_handle,
- (int) reloc->read_domains,
- (int) reloc->write_domain,
- (int) target_obj_priv->gtt_offset,
- (int) reloc->presumed_offset,
- reloc->delta);
+ (int) reloc.offset,
+ (int) reloc.target_handle,
+ (int) reloc.read_domains,
+ (int) reloc.write_domain,
+ (int) target_offset,
+ (int) reloc.presumed_offset,
+ reloc.delta);
#endif
/* The target buffer should have appeared before us in the
* exec_object list, so it should have a GTT space bound by now.
*/
- if (target_obj_priv->gtt_space == NULL) {
+ if (target_offset == 0) {
DRM_ERROR("No GTT space found for object %d\n",
- reloc->target_handle);
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
+ reloc.target_handle);
+ ret = -EINVAL;
+ break;
}
/* Validate that the target is in a valid r/w GPU domain */
- if (reloc->write_domain & (reloc->write_domain - 1)) {
+ if (reloc.write_domain & (reloc.write_domain - 1)) {
DRM_ERROR("reloc with multiple write domains: "
"obj %p target %d offset %d "
"read %08x write %08x",
- obj, reloc->target_handle,
- (int) reloc->offset,
- reloc->read_domains,
- reloc->write_domain);
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
+ obj, reloc.target_handle,
+ (int) reloc.offset,
+ reloc.read_domains,
+ reloc.write_domain);
+ ret = -EINVAL;
+ break;
}
- if (reloc->write_domain & I915_GEM_DOMAIN_CPU ||
- reloc->read_domains & I915_GEM_DOMAIN_CPU) {
+ if (reloc.write_domain & I915_GEM_DOMAIN_CPU ||
+ reloc.read_domains & I915_GEM_DOMAIN_CPU) {
DRM_ERROR("reloc with read/write CPU domains: "
"obj %p target %d offset %d "
"read %08x write %08x",
- obj, reloc->target_handle,
- (int) reloc->offset,
- reloc->read_domains,
- reloc->write_domain);
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
+ obj, reloc.target_handle,
+ (int) reloc.offset,
+ reloc.read_domains,
+ reloc.write_domain);
+ ret = -EINVAL;
+ break;
}
- if (reloc->write_domain && target_obj->pending_write_domain &&
- reloc->write_domain != target_obj->pending_write_domain) {
+ if (reloc.write_domain && target_obj->pending_write_domain &&
+ reloc.write_domain != target_obj->pending_write_domain) {
DRM_ERROR("Write domain conflict: "
"obj %p target %d offset %d "
"new %08x old %08x\n",
- obj, reloc->target_handle,
- (int) reloc->offset,
- reloc->write_domain,
+ obj, reloc.target_handle,
+ (int) reloc.offset,
+ reloc.write_domain,
target_obj->pending_write_domain);
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
- target_obj->pending_read_domains |= reloc->read_domains;
- target_obj->pending_write_domain |= reloc->write_domain;
+ target_obj->pending_read_domains |= reloc.read_domains;
+ target_obj->pending_write_domain |= reloc.write_domain;
/* If the relocation already has the right value in it, no
* more work needs to be done.
*/
- if (target_obj_priv->gtt_offset == reloc->presumed_offset) {
- drm_gem_object_unreference(target_obj);
+ if (target_offset == reloc.presumed_offset)
continue;
- }
/* Check that the relocation address is valid... */
- if (reloc->offset > obj->size - 4) {
+ if (reloc.offset > obj->base.size - 4) {
DRM_ERROR("Relocation beyond object bounds: "
"obj %p target %d offset %d size %d.\n",
- obj, reloc->target_handle,
- (int) reloc->offset, (int) obj->size);
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
+ obj, reloc.target_handle,
+ (int) reloc.offset, (int) obj->base.size);
+ ret = -EINVAL;
+ break;
}
- if (reloc->offset & 3) {
+ if (reloc.offset & 3) {
DRM_ERROR("Relocation not 4-byte aligned: "
"obj %p target %d offset %d.\n",
- obj, reloc->target_handle,
- (int) reloc->offset);
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
+ obj, reloc.target_handle,
+ (int) reloc.offset);
+ ret = -EINVAL;
+ break;
}
/* and points to somewhere within the target object. */
- if (reloc->delta >= target_obj->size) {
+ if (reloc.delta >= target_obj->size) {
DRM_ERROR("Relocation beyond target object bounds: "
"obj %p target %d delta %d size %d.\n",
- obj, reloc->target_handle,
- (int) reloc->delta, (int) target_obj->size);
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
- }
-
- ret = i915_gem_object_set_to_gtt_domain(obj, 1);
- if (ret != 0) {
- drm_gem_object_unreference(target_obj);
- i915_gem_object_unpin(obj);
- return -EINVAL;
+ obj, reloc.target_handle,
+ (int) reloc.delta, (int) target_obj->size);
+ ret = -EINVAL;
+ break;
}
- /* Map the page containing the relocation we're going to
- * perform.
- */
- reloc_offset = obj_priv->gtt_offset + reloc->offset;
- reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
- (reloc_offset &
- ~(PAGE_SIZE - 1)),
- KM_USER0);
- reloc_entry = (uint32_t __iomem *)(reloc_page +
- (reloc_offset & (PAGE_SIZE - 1)));
- reloc_val = target_obj_priv->gtt_offset + reloc->delta;
-
-#if WATCH_BUF
- DRM_INFO("Applied relocation: %p@0x%08x %08x -> %08x\n",
- obj, (unsigned int) reloc->offset,
- readl(reloc_entry), reloc_val);
-#endif
- writel(reloc_val, reloc_entry);
- io_mapping_unmap_atomic(reloc_page, KM_USER0);
-
- /* The updated presumed offset for this entry will be
- * copied back out to the user.
- */
- reloc->presumed_offset = target_obj_priv->gtt_offset;
-
- drm_gem_object_unreference(target_obj);
- }
-
-#if WATCH_BUF
- if (0)
- i915_gem_dump_object(obj, 128, __func__, ~0);
-#endif
- return 0;
-}
+ reloc.delta += target_offset;
+ if (obj->base.write_domain == I915_GEM_DOMAIN_CPU) {
+ uint32_t page_offset = reloc.offset & ~PAGE_MASK;
+ char *vaddr;
-/* Throttle our rendering by waiting until the ring has completed our requests
- * emitted over 20 msec ago.
- *
- * Note that if we were to use the current jiffies each time around the loop,
- * we wouldn't escape the function with any frames outstanding if the time to
- * render a frame was over 20ms.
- *
- * This should get us reasonable parallelism between CPU and GPU but also
- * relatively low latency when blocking on a particular request to finish.
- */
-static int
-i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
-{
- struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
- int ret = 0;
- unsigned long recent_enough = jiffies - msecs_to_jiffies(20);
-
- mutex_lock(&dev->struct_mutex);
- while (!list_empty(&i915_file_priv->mm.request_list)) {
- struct drm_i915_gem_request *request;
+ vaddr = kmap_atomic(obj->pages[reloc.offset >> PAGE_SHIFT]);
+ *(uint32_t *)(vaddr + page_offset) = reloc.delta;
+ kunmap_atomic(vaddr);
+ } else {
+ uint32_t __iomem *reloc_entry;
+ void __iomem *reloc_page;
- request = list_first_entry(&i915_file_priv->mm.request_list,
- struct drm_i915_gem_request,
- client_list);
+ ret = i915_gem_object_set_to_gtt_domain(&obj->base, 1);
+ if (ret)
+ break;
- if (time_after_eq(request->emitted_jiffies, recent_enough))
- break;
+ /* Map the page containing the relocation we're going to perform. */
+ reloc.offset += obj->gtt_offset;
+ reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+ reloc.offset & PAGE_MASK);
+ reloc_entry = (uint32_t __iomem *)
+ (reloc_page + (reloc.offset & ~PAGE_MASK));
+ iowrite32(reloc.delta, reloc_entry);
+ io_mapping_unmap_atomic(reloc_page);
+ }
- ret = i915_wait_request(dev, request->seqno, request->ring);
- if (ret != 0)
- break;
+ /* and update the user's relocation entry */
+ reloc.presumed_offset = target_offset;
+ if (__copy_to_user_inatomic(&user_relocs[i].presumed_offset,
+ &reloc.presumed_offset,
+ sizeof(reloc.presumed_offset))) {
+ ret = -EFAULT;
+ break;
+ }
}
- mutex_unlock(&dev->struct_mutex);
+ drm_gem_object_unreference(target_obj);
return ret;
}
static int
-i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object2 *exec_list,
- uint32_t buffer_count,
- struct drm_i915_gem_relocation_entry **relocs)
+i915_gem_execbuffer_pin(struct drm_device *dev,
+ struct drm_file *file,
+ struct drm_gem_object **object_list,
+ struct drm_i915_gem_exec_object2 *exec_list,
+ int count)
{
- uint32_t reloc_count = 0, reloc_index = 0, i;
- int ret;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret, i, retry;
- *relocs = NULL;
- for (i = 0; i < buffer_count; i++) {
- if (reloc_count + exec_list[i].relocation_count < reloc_count)
- return -EINVAL;
- reloc_count += exec_list[i].relocation_count;
- }
+ /* attempt to pin all of the buffers into the GTT */
+ for (retry = 0; retry < 2; retry++) {
+ ret = 0;
+ for (i = 0; i < count; i++) {
+ struct drm_i915_gem_exec_object2 *entry = &exec_list[i];
+ struct drm_i915_gem_object *obj= to_intel_bo(object_list[i]);
+ bool need_fence =
+ entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
+ obj->tiling_mode != I915_TILING_NONE;
+
+ /* Check fence reg constraints and rebind if necessary */
+ if (need_fence &&
+ !i915_gem_object_fence_offset_ok(&obj->base,
+ obj->tiling_mode)) {
+ ret = i915_gem_object_unbind(&obj->base);
+ if (ret)
+ break;
+ }
- *relocs = drm_calloc_large(reloc_count, sizeof(**relocs));
- if (*relocs == NULL) {
- DRM_ERROR("failed to alloc relocs, count %d\n", reloc_count);
- return -ENOMEM;
- }
+ ret = i915_gem_object_pin(&obj->base, entry->alignment);
+ if (ret)
+ break;
- for (i = 0; i < buffer_count; i++) {
- struct drm_i915_gem_relocation_entry __user *user_relocs;
+ /*
+ * Pre-965 chips need a fence register set up in order
+ * to properly handle blits to/from tiled surfaces.
+ */
+ if (need_fence) {
+ ret = i915_gem_object_get_fence_reg(&obj->base, true);
+ if (ret) {
+ i915_gem_object_unpin(&obj->base);
+ break;
+ }
- user_relocs = (void __user *)(uintptr_t)exec_list[i].relocs_ptr;
+ dev_priv->fence_regs[obj->fence_reg].gpu = true;
+ }
- ret = copy_from_user(&(*relocs)[reloc_index],
- user_relocs,
- exec_list[i].relocation_count *
- sizeof(**relocs));
- if (ret != 0) {
- drm_free_large(*relocs);
- *relocs = NULL;
- return -EFAULT;
+ entry->offset = obj->gtt_offset;
}
- reloc_index += exec_list[i].relocation_count;
+ while (i--)
+ i915_gem_object_unpin(object_list[i]);
+
+ if (ret == 0)
+ break;
+
+ if (ret != -ENOSPC || retry)
+ return ret;
+
+ ret = i915_gem_evict_everything(dev);
+ if (ret)
+ return ret;
}
return 0;
}
+/* Throttle our rendering by waiting until the ring has completed our requests
+ * emitted over 20 msec ago.
+ *
+ * Note that if we were to use the current jiffies each time around the loop,
+ * we wouldn't escape the function with any frames outstanding if the time to
+ * render a frame was over 20ms.
+ *
+ * This should get us reasonable parallelism between CPU and GPU but also
+ * relatively low latency when blocking on a particular request to finish.
+ */
static int
-i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object2 *exec_list,
- uint32_t buffer_count,
- struct drm_i915_gem_relocation_entry *relocs)
+i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
{
- uint32_t reloc_count = 0, i;
- int ret = 0;
-
- if (relocs == NULL)
- return 0;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+ unsigned long recent_enough = jiffies - msecs_to_jiffies(20);
+ struct drm_i915_gem_request *request;
+ struct intel_ring_buffer *ring = NULL;
+ u32 seqno = 0;
+ int ret;
- for (i = 0; i < buffer_count; i++) {
- struct drm_i915_gem_relocation_entry __user *user_relocs;
- int unwritten;
+ spin_lock(&file_priv->mm.lock);
+ list_for_each_entry(request, &file_priv->mm.request_list, client_list) {
+ if (time_after_eq(request->emitted_jiffies, recent_enough))
+ break;
- user_relocs = (void __user *)(uintptr_t)exec_list[i].relocs_ptr;
+ ring = request->ring;
+ seqno = request->seqno;
+ }
+ spin_unlock(&file_priv->mm.lock);
- unwritten = copy_to_user(user_relocs,
- &relocs[reloc_count],
- exec_list[i].relocation_count *
- sizeof(*relocs));
+ if (seqno == 0)
+ return 0;
- if (unwritten) {
- ret = -EFAULT;
- goto err;
- }
+ ret = 0;
+ if (!i915_seqno_passed(ring->get_seqno(dev, ring), seqno)) {
+ /* And wait for the seqno passing without holding any locks and
+ * causing extra latency for others. This is safe as the irq
+ * generation is designed to be run atomically and so is
+ * lockless.
+ */
+ ring->user_irq_get(dev, ring);
+ ret = wait_event_interruptible(ring->irq_queue,
+ i915_seqno_passed(ring->get_seqno(dev, ring), seqno)
+ || atomic_read(&dev_priv->mm.wedged));
+ ring->user_irq_put(dev, ring);
- reloc_count += exec_list[i].relocation_count;
+ if (ret == 0 && atomic_read(&dev_priv->mm.wedged))
+ ret = -EIO;
}
-err:
- drm_free_large(relocs);
+ if (ret == 0)
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0);
return ret;
}
static int
-i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer2 *exec,
- uint64_t exec_offset)
+i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec,
+ uint64_t exec_offset)
{
uint32_t exec_start, exec_len;
@@ -3509,44 +3573,32 @@ i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer2 *exec,
}
static int
-i915_gem_wait_for_pending_flip(struct drm_device *dev,
- struct drm_gem_object **object_list,
- int count)
+validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
+ int count)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj_priv;
- DEFINE_WAIT(wait);
- int i, ret = 0;
+ int i;
- for (;;) {
- prepare_to_wait(&dev_priv->pending_flip_queue,
- &wait, TASK_INTERRUPTIBLE);
- for (i = 0; i < count; i++) {
- obj_priv = to_intel_bo(object_list[i]);
- if (atomic_read(&obj_priv->pending_flip) > 0)
- break;
- }
- if (i == count)
- break;
+ for (i = 0; i < count; i++) {
+ char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr;
+ size_t length = exec[i].relocation_count * sizeof(struct drm_i915_gem_relocation_entry);
- if (!signal_pending(current)) {
- mutex_unlock(&dev->struct_mutex);
- schedule();
- mutex_lock(&dev->struct_mutex);
- continue;
- }
- ret = -ERESTARTSYS;
- break;
+ if (!access_ok(VERIFY_READ, ptr, length))
+ return -EFAULT;
+
+ /* we may also need to update the presumed offsets */
+ if (!access_ok(VERIFY_WRITE, ptr, length))
+ return -EFAULT;
+
+ if (fault_in_pages_readable(ptr, length))
+ return -EFAULT;
}
- finish_wait(&dev_priv->pending_flip_queue, &wait);
- return ret;
+ return 0;
}
-
-int
+static int
i915_gem_do_execbuffer(struct drm_device *dev, void *data,
- struct drm_file *file_priv,
+ struct drm_file *file,
struct drm_i915_gem_execbuffer2 *args,
struct drm_i915_gem_exec_object2 *exec_list)
{
@@ -3555,26 +3607,47 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
struct drm_gem_object *batch_obj;
struct drm_i915_gem_object *obj_priv;
struct drm_clip_rect *cliprects = NULL;
- struct drm_i915_gem_relocation_entry *relocs = NULL;
- int ret = 0, ret2, i, pinned = 0;
+ struct drm_i915_gem_request *request = NULL;
+ int ret, i, flips;
uint64_t exec_offset;
- uint32_t seqno, flush_domains, reloc_index;
- int pin_tries, flips;
struct intel_ring_buffer *ring = NULL;
+ ret = i915_gem_check_is_wedged(dev);
+ if (ret)
+ return ret;
+
+ ret = validate_exec_list(exec_list, args->buffer_count);
+ if (ret)
+ return ret;
+
#if WATCH_EXEC
DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
(int) args->buffers_ptr, args->buffer_count, args->batch_len);
#endif
- if (args->flags & I915_EXEC_BSD) {
+ switch (args->flags & I915_EXEC_RING_MASK) {
+ case I915_EXEC_DEFAULT:
+ case I915_EXEC_RENDER:
+ ring = &dev_priv->render_ring;
+ break;
+ case I915_EXEC_BSD:
if (!HAS_BSD(dev)) {
- DRM_ERROR("execbuf with wrong flag\n");
+ DRM_ERROR("execbuf with invalid ring (BSD)\n");
return -EINVAL;
}
ring = &dev_priv->bsd_ring;
- } else {
- ring = &dev_priv->render_ring;
+ break;
+ case I915_EXEC_BLT:
+ if (!HAS_BLT(dev)) {
+ DRM_ERROR("execbuf with invalid ring (BLT)\n");
+ return -EINVAL;
+ }
+ ring = &dev_priv->blt_ring;
+ break;
+ default:
+ DRM_ERROR("execbuf with unknown ring: %d\n",
+ (int)(args->flags & I915_EXEC_RING_MASK));
+ return -EINVAL;
}
if (args->buffer_count < 1) {
@@ -3609,20 +3682,15 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
}
}
- ret = i915_gem_get_relocs_from_user(exec_list, args->buffer_count,
- &relocs);
- if (ret != 0)
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (request == NULL) {
+ ret = -ENOMEM;
goto pre_mutex_err;
+ }
- mutex_lock(&dev->struct_mutex);
-
- i915_verify_inactive(dev, __FILE__, __LINE__);
-
- if (atomic_read(&dev_priv->mm.wedged)) {
- mutex_unlock(&dev->struct_mutex);
- ret = -EIO;
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
goto pre_mutex_err;
- }
if (dev_priv->mm.suspended) {
mutex_unlock(&dev->struct_mutex);
@@ -3631,9 +3699,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
}
/* Look up object handles */
- flips = 0;
for (i = 0; i < args->buffer_count; i++) {
- object_list[i] = drm_gem_object_lookup(dev, file_priv,
+ object_list[i] = drm_gem_object_lookup(dev, file,
exec_list[i].handle);
if (object_list[i] == NULL) {
DRM_ERROR("Invalid object handle %d at index %d\n",
@@ -3654,75 +3721,22 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
goto err;
}
obj_priv->in_execbuffer = true;
- flips += atomic_read(&obj_priv->pending_flip);
}
- if (flips > 0) {
- ret = i915_gem_wait_for_pending_flip(dev, object_list,
- args->buffer_count);
- if (ret)
- goto err;
- }
-
- /* Pin and relocate */
- for (pin_tries = 0; ; pin_tries++) {
- ret = 0;
- reloc_index = 0;
-
- for (i = 0; i < args->buffer_count; i++) {
- object_list[i]->pending_read_domains = 0;
- object_list[i]->pending_write_domain = 0;
- ret = i915_gem_object_pin_and_relocate(object_list[i],
- file_priv,
- &exec_list[i],
- &relocs[reloc_index]);
- if (ret)
- break;
- pinned = i + 1;
- reloc_index += exec_list[i].relocation_count;
- }
- /* success */
- if (ret == 0)
- break;
-
- /* error other than GTT full, or we've already tried again */
- if (ret != -ENOSPC || pin_tries >= 1) {
- if (ret != -ERESTARTSYS) {
- unsigned long long total_size = 0;
- int num_fences = 0;
- for (i = 0; i < args->buffer_count; i++) {
- obj_priv = to_intel_bo(object_list[i]);
-
- total_size += object_list[i]->size;
- num_fences +=
- exec_list[i].flags & EXEC_OBJECT_NEEDS_FENCE &&
- obj_priv->tiling_mode != I915_TILING_NONE;
- }
- DRM_ERROR("Failed to pin buffer %d of %d, total %llu bytes, %d fences: %d\n",
- pinned+1, args->buffer_count,
- total_size, num_fences,
- ret);
- DRM_ERROR("%d objects [%d pinned], "
- "%d object bytes [%d pinned], "
- "%d/%d gtt bytes\n",
- atomic_read(&dev->object_count),
- atomic_read(&dev->pin_count),
- atomic_read(&dev->object_memory),
- atomic_read(&dev->pin_memory),
- atomic_read(&dev->gtt_memory),
- dev->gtt_total);
- }
- goto err;
- }
-
- /* unpin all of our buffers */
- for (i = 0; i < pinned; i++)
- i915_gem_object_unpin(object_list[i]);
- pinned = 0;
+ /* Move the objects en-masse into the GTT, evicting if necessary. */
+ ret = i915_gem_execbuffer_pin(dev, file,
+ object_list, exec_list,
+ args->buffer_count);
+ if (ret)
+ goto err;
- /* evict everyone we can from the aperture */
- ret = i915_gem_evict_everything(dev);
- if (ret && ret != -ENOSPC)
+ /* The objects are in their final locations, apply the relocations. */
+ for (i = 0; i < args->buffer_count; i++) {
+ struct drm_i915_gem_object *obj = to_intel_bo(object_list[i]);
+ obj->base.pending_read_domains = 0;
+ obj->base.pending_write_domain = 0;
+ ret = i915_gem_execbuffer_relocate(obj, file, &exec_list[i]);
+ if (ret)
goto err;
}
@@ -3735,33 +3749,29 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
}
batch_obj->pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
- /* Sanity check the batch buffer, prior to moving objects */
- exec_offset = exec_list[args->buffer_count - 1].offset;
- ret = i915_gem_check_execbuffer (args, exec_offset);
+ /* Sanity check the batch buffer */
+ exec_offset = to_intel_bo(batch_obj)->gtt_offset;
+ ret = i915_gem_check_execbuffer(args, exec_offset);
if (ret != 0) {
DRM_ERROR("execbuf with invalid offset/length\n");
goto err;
}
- i915_verify_inactive(dev, __FILE__, __LINE__);
-
/* Zero the global flush/invalidate flags. These
* will be modified as new domains are computed
* for each object
*/
dev->invalidate_domains = 0;
dev->flush_domains = 0;
- dev_priv->flush_rings = 0;
+ dev_priv->mm.flush_rings = 0;
for (i = 0; i < args->buffer_count; i++) {
struct drm_gem_object *obj = object_list[i];
/* Compute new gpu domains and update invalidate/flush */
- i915_gem_object_set_to_gpu_domain(obj);
+ i915_gem_object_set_to_gpu_domain(obj, ring);
}
- i915_verify_inactive(dev, __FILE__, __LINE__);
-
if (dev->invalidate_domains | dev->flush_domains) {
#if WATCH_EXEC
DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n",
@@ -3769,38 +3779,21 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
dev->invalidate_domains,
dev->flush_domains);
#endif
- i915_gem_flush(dev,
+ i915_gem_flush(dev, file,
dev->invalidate_domains,
- dev->flush_domains);
- if (dev_priv->flush_rings & FLUSH_RENDER_RING)
- (void)i915_add_request(dev, file_priv,
- dev->flush_domains,
- &dev_priv->render_ring);
- if (dev_priv->flush_rings & FLUSH_BSD_RING)
- (void)i915_add_request(dev, file_priv,
- dev->flush_domains,
- &dev_priv->bsd_ring);
+ dev->flush_domains,
+ dev_priv->mm.flush_rings);
}
for (i = 0; i < args->buffer_count; i++) {
struct drm_gem_object *obj = object_list[i];
- struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
uint32_t old_write_domain = obj->write_domain;
-
obj->write_domain = obj->pending_write_domain;
- if (obj->write_domain)
- list_move_tail(&obj_priv->gpu_write_list,
- &dev_priv->mm.gpu_write_list);
- else
- list_del_init(&obj_priv->gpu_write_list);
-
trace_i915_gem_object_change_domain(obj,
obj->read_domains,
old_write_domain);
}
- i915_verify_inactive(dev, __FILE__, __LINE__);
-
#if WATCH_COHERENCY
for (i = 0; i < args->buffer_count; i++) {
i915_gem_object_check_coherency(object_list[i],
@@ -3815,9 +3808,38 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
~0);
#endif
+ /* Check for any pending flips. As we only maintain a flip queue depth
+ * of 1, we can simply insert a WAIT for the next display flip prior
+ * to executing the batch and avoid stalling the CPU.
+ */
+ flips = 0;
+ for (i = 0; i < args->buffer_count; i++) {
+ if (object_list[i]->write_domain)
+ flips |= atomic_read(&to_intel_bo(object_list[i])->pending_flip);
+ }
+ if (flips) {
+ int plane, flip_mask;
+
+ for (plane = 0; flips >> plane; plane++) {
+ if (((flips >> plane) & 1) == 0)
+ continue;
+
+ if (plane)
+ flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
+ else
+ flip_mask = MI_WAIT_FOR_PLANE_A_FLIP;
+
+ intel_ring_begin(dev, ring, 2);
+ intel_ring_emit(dev, ring,
+ MI_WAIT_FOR_EVENT | flip_mask);
+ intel_ring_emit(dev, ring, MI_NOOP);
+ intel_ring_advance(dev, ring);
+ }
+ }
+
/* Exec the batchbuffer */
ret = ring->dispatch_gem_execbuffer(dev, ring, args,
- cliprects, exec_offset);
+ cliprects, exec_offset);
if (ret) {
DRM_ERROR("dispatch failed %d\n", ret);
goto err;
@@ -3827,38 +3849,21 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
* Ensure that the commands in the batch buffer are
* finished before the interrupt fires
*/
- flush_domains = i915_retire_commands(dev, ring);
-
- i915_verify_inactive(dev, __FILE__, __LINE__);
+ i915_retire_commands(dev, ring);
- /*
- * Get a seqno representing the execution of the current buffer,
- * which we can wait on. We would like to mitigate these interrupts,
- * likely by only creating seqnos occasionally (so that we have
- * *some* interrupts representing completion of buffers that we can
- * wait on when trying to clear up gtt space).
- */
- seqno = i915_add_request(dev, file_priv, flush_domains, ring);
- BUG_ON(seqno == 0);
for (i = 0; i < args->buffer_count; i++) {
struct drm_gem_object *obj = object_list[i];
- obj_priv = to_intel_bo(obj);
- i915_gem_object_move_to_active(obj, seqno, ring);
-#if WATCH_LRU
- DRM_INFO("%s: move to exec list %p\n", __func__, obj);
-#endif
+ i915_gem_object_move_to_active(obj, ring);
+ if (obj->write_domain)
+ list_move_tail(&to_intel_bo(obj)->gpu_write_list,
+ &ring->gpu_write_list);
}
-#if WATCH_LRU
- i915_dump_lru(dev, __func__);
-#endif
- i915_verify_inactive(dev, __FILE__, __LINE__);
+ i915_add_request(dev, file, request, ring);
+ request = NULL;
err:
- for (i = 0; i < pinned; i++)
- i915_gem_object_unpin(object_list[i]);
-
for (i = 0; i < args->buffer_count; i++) {
if (object_list[i]) {
obj_priv = to_intel_bo(object_list[i]);
@@ -3870,22 +3875,9 @@ err:
mutex_unlock(&dev->struct_mutex);
pre_mutex_err:
- /* Copy the updated relocations out regardless of current error
- * state. Failure to update the relocs would mean that the next
- * time userland calls execbuf, it would do so with presumed offset
- * state that didn't match the actual object state.
- */
- ret2 = i915_gem_put_relocs_to_user(exec_list, args->buffer_count,
- relocs);
- if (ret2 != 0) {
- DRM_ERROR("Failed to copy relocations back out: %d\n", ret2);
-
- if (ret == 0)
- ret = ret2;
- }
-
drm_free_large(object_list);
kfree(cliprects);
+ kfree(request);
return ret;
}
@@ -3942,7 +3934,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
exec2_list[i].relocs_ptr = exec_list[i].relocs_ptr;
exec2_list[i].alignment = exec_list[i].alignment;
exec2_list[i].offset = exec_list[i].offset;
- if (!IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen < 4)
exec2_list[i].flags = EXEC_OBJECT_NEEDS_FENCE;
else
exec2_list[i].flags = 0;
@@ -4039,12 +4031,12 @@ int
i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
{
struct drm_device *dev = obj->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
int ret;
BUG_ON(obj_priv->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT);
-
- i915_verify_inactive(dev, __FILE__, __LINE__);
+ WARN_ON(i915_verify_lists(dev));
if (obj_priv->gtt_space != NULL) {
if (alignment == 0)
@@ -4072,14 +4064,13 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
* remove it from the inactive list
*/
if (obj_priv->pin_count == 1) {
- atomic_inc(&dev->pin_count);
- atomic_add(obj->size, &dev->pin_memory);
- if (!obj_priv->active &&
- (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0)
- list_del_init(&obj_priv->list);
+ i915_gem_info_add_pin(dev_priv, obj->size);
+ if (!obj_priv->active)
+ list_move_tail(&obj_priv->mm_list,
+ &dev_priv->mm.pinned_list);
}
- i915_verify_inactive(dev, __FILE__, __LINE__);
+ WARN_ON(i915_verify_lists(dev));
return 0;
}
@@ -4090,7 +4081,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj)
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
- i915_verify_inactive(dev, __FILE__, __LINE__);
+ WARN_ON(i915_verify_lists(dev));
obj_priv->pin_count--;
BUG_ON(obj_priv->pin_count < 0);
BUG_ON(obj_priv->gtt_space == NULL);
@@ -4100,14 +4091,12 @@ i915_gem_object_unpin(struct drm_gem_object *obj)
* the inactive list
*/
if (obj_priv->pin_count == 0) {
- if (!obj_priv->active &&
- (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0)
- list_move_tail(&obj_priv->list,
+ if (!obj_priv->active)
+ list_move_tail(&obj_priv->mm_list,
&dev_priv->mm.inactive_list);
- atomic_dec(&dev->pin_count);
- atomic_sub(obj->size, &dev->pin_memory);
+ i915_gem_info_remove_pin(dev_priv, obj->size);
}
- i915_verify_inactive(dev, __FILE__, __LINE__);
+ WARN_ON(i915_verify_lists(dev));
}
int
@@ -4119,41 +4108,36 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_object *obj_priv;
int ret;
- mutex_lock(&dev->struct_mutex);
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
- DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n",
- args->handle);
- mutex_unlock(&dev->struct_mutex);
- return -ENOENT;
+ ret = -ENOENT;
+ goto unlock;
}
obj_priv = to_intel_bo(obj);
if (obj_priv->madv != I915_MADV_WILLNEED) {
DRM_ERROR("Attempting to pin a purgeable buffer\n");
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) {
DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n",
args->handle);
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
obj_priv->user_pin_count++;
obj_priv->pin_filp = file_priv;
if (obj_priv->user_pin_count == 1) {
ret = i915_gem_object_pin(obj, args->alignment);
- if (ret != 0) {
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return ret;
- }
+ if (ret)
+ goto out;
}
/* XXX - flush the CPU caches for pinned objects
@@ -4161,10 +4145,11 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
*/
i915_gem_object_flush_cpu_write_domain(obj);
args->offset = obj_priv->gtt_offset;
+out:
drm_gem_object_unreference(obj);
+unlock:
mutex_unlock(&dev->struct_mutex);
-
- return 0;
+ return ret;
}
int
@@ -4174,24 +4159,24 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_pin *args = data;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
+ int ret;
- mutex_lock(&dev->struct_mutex);
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
- DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n",
- args->handle);
- mutex_unlock(&dev->struct_mutex);
- return -ENOENT;
+ ret = -ENOENT;
+ goto unlock;
}
-
obj_priv = to_intel_bo(obj);
+
if (obj_priv->pin_filp != file_priv) {
DRM_ERROR("Not pinned by caller in i915_gem_pin_ioctl(): %d\n",
args->handle);
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
obj_priv->user_pin_count--;
if (obj_priv->user_pin_count == 0) {
@@ -4199,9 +4184,11 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
i915_gem_object_unpin(obj);
}
+out:
drm_gem_object_unreference(obj);
+unlock:
mutex_unlock(&dev->struct_mutex);
- return 0;
+ return ret;
}
int
@@ -4211,22 +4198,24 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_busy *args = data;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
+ int ret;
+
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
- DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n",
- args->handle);
- return -ENOENT;
+ ret = -ENOENT;
+ goto unlock;
}
-
- mutex_lock(&dev->struct_mutex);
+ obj_priv = to_intel_bo(obj);
/* Count all active objects as busy, even if they are currently not used
* by the gpu. Users of this interface expect objects to eventually
* become non-busy without any further actions, therefore emit any
* necessary flushes here.
*/
- obj_priv = to_intel_bo(obj);
args->busy = obj_priv->active;
if (args->busy) {
/* Unconditionally flush objects, even when the gpu still uses this
@@ -4234,10 +4223,10 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
* use this buffer rather sooner than later, so issuing the required
* flush earlier is beneficial.
*/
- if (obj->write_domain) {
- i915_gem_flush(dev, 0, obj->write_domain);
- (void)i915_add_request(dev, file_priv, obj->write_domain, obj_priv->ring);
- }
+ if (obj->write_domain & I915_GEM_GPU_DOMAINS)
+ i915_gem_flush_ring(dev, file_priv,
+ obj_priv->ring,
+ 0, obj->write_domain);
/* Update the active list for the hardware's current position.
* Otherwise this only updates on a delayed timer or when irqs
@@ -4250,8 +4239,9 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
}
drm_gem_object_unreference(obj);
+unlock:
mutex_unlock(&dev->struct_mutex);
- return 0;
+ return ret;
}
int
@@ -4268,6 +4258,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_madvise *args = data;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
+ int ret;
switch (args->madv) {
case I915_MADV_DONTNEED:
@@ -4277,22 +4268,20 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
- DRM_ERROR("Bad handle in i915_gem_madvise_ioctl(): %d\n",
- args->handle);
- return -ENOENT;
+ ret = -ENOENT;
+ goto unlock;
}
-
- mutex_lock(&dev->struct_mutex);
obj_priv = to_intel_bo(obj);
if (obj_priv->pin_count) {
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
-
- DRM_ERROR("Attempted i915_gem_madvise_ioctl() on a pinned object\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (obj_priv->madv != __I915_MADV_PURGED)
@@ -4305,15 +4294,17 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
args->retained = obj_priv->madv != __I915_MADV_PURGED;
+out:
drm_gem_object_unreference(obj);
+unlock:
mutex_unlock(&dev->struct_mutex);
-
- return 0;
+ return ret;
}
struct drm_gem_object * i915_gem_alloc_object(struct drm_device *dev,
size_t size)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
@@ -4325,18 +4316,19 @@ struct drm_gem_object * i915_gem_alloc_object(struct drm_device *dev,
return NULL;
}
+ i915_gem_info_add_obj(dev_priv, size);
+
obj->base.write_domain = I915_GEM_DOMAIN_CPU;
obj->base.read_domains = I915_GEM_DOMAIN_CPU;
obj->agp_type = AGP_USER_MEMORY;
obj->base.driver_private = NULL;
obj->fence_reg = I915_FENCE_REG_NONE;
- INIT_LIST_HEAD(&obj->list);
+ INIT_LIST_HEAD(&obj->mm_list);
+ INIT_LIST_HEAD(&obj->ring_list);
INIT_LIST_HEAD(&obj->gpu_write_list);
obj->madv = I915_MADV_WILLNEED;
- trace_i915_gem_object_create(&obj->base);
-
return &obj->base;
}
@@ -4356,7 +4348,7 @@ static void i915_gem_free_object_tail(struct drm_gem_object *obj)
ret = i915_gem_object_unbind(obj);
if (ret == -ERESTARTSYS) {
- list_move(&obj_priv->list,
+ list_move(&obj_priv->mm_list,
&dev_priv->mm.deferred_free_list);
return;
}
@@ -4365,6 +4357,7 @@ static void i915_gem_free_object_tail(struct drm_gem_object *obj)
i915_gem_free_mmap_offset(obj);
drm_gem_object_release(obj);
+ i915_gem_info_remove_obj(dev_priv, obj->size);
kfree(obj_priv->page_cpu_valid);
kfree(obj_priv->bit_17);
@@ -4395,10 +4388,7 @@ i915_gem_idle(struct drm_device *dev)
mutex_lock(&dev->struct_mutex);
- if (dev_priv->mm.suspended ||
- (dev_priv->render_ring.gem_object == NULL) ||
- (HAS_BSD(dev) &&
- dev_priv->bsd_ring.gem_object == NULL)) {
+ if (dev_priv->mm.suspended) {
mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -4423,7 +4413,7 @@ i915_gem_idle(struct drm_device *dev)
* And not confound mm.suspended!
*/
dev_priv->mm.suspended = 1;
- del_timer(&dev_priv->hangcheck_timer);
+ del_timer_sync(&dev_priv->hangcheck_timer);
i915_kernel_lost_context(dev);
i915_gem_cleanup_ringbuffer(dev);
@@ -4503,36 +4493,34 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
- dev_priv->render_ring = render_ring;
-
- if (!I915_NEED_GFX_HWS(dev)) {
- dev_priv->render_ring.status_page.page_addr
- = dev_priv->status_page_dmah->vaddr;
- memset(dev_priv->render_ring.status_page.page_addr,
- 0, PAGE_SIZE);
- }
-
if (HAS_PIPE_CONTROL(dev)) {
ret = i915_gem_init_pipe_control(dev);
if (ret)
return ret;
}
- ret = intel_init_ring_buffer(dev, &dev_priv->render_ring);
+ ret = intel_init_render_ring_buffer(dev);
if (ret)
goto cleanup_pipe_control;
if (HAS_BSD(dev)) {
- dev_priv->bsd_ring = bsd_ring;
- ret = intel_init_ring_buffer(dev, &dev_priv->bsd_ring);
+ ret = intel_init_bsd_ring_buffer(dev);
if (ret)
goto cleanup_render_ring;
}
+ if (HAS_BLT(dev)) {
+ ret = intel_init_blt_ring_buffer(dev);
+ if (ret)
+ goto cleanup_bsd_ring;
+ }
+
dev_priv->next_seqno = 1;
return 0;
+cleanup_bsd_ring:
+ intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
cleanup_render_ring:
intel_cleanup_ring_buffer(dev, &dev_priv->render_ring);
cleanup_pipe_control:
@@ -4547,8 +4535,8 @@ i915_gem_cleanup_ringbuffer(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
intel_cleanup_ring_buffer(dev, &dev_priv->render_ring);
- if (HAS_BSD(dev))
- intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
+ intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
+ intel_cleanup_ring_buffer(dev, &dev_priv->blt_ring);
if (HAS_PIPE_CONTROL(dev))
i915_gem_cleanup_pipe_control(dev);
}
@@ -4577,15 +4565,15 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
return ret;
}
- spin_lock(&dev_priv->mm.active_list_lock);
+ BUG_ON(!list_empty(&dev_priv->mm.active_list));
BUG_ON(!list_empty(&dev_priv->render_ring.active_list));
- BUG_ON(HAS_BSD(dev) && !list_empty(&dev_priv->bsd_ring.active_list));
- spin_unlock(&dev_priv->mm.active_list_lock);
-
+ BUG_ON(!list_empty(&dev_priv->bsd_ring.active_list));
+ BUG_ON(!list_empty(&dev_priv->blt_ring.active_list));
BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
BUG_ON(!list_empty(&dev_priv->mm.inactive_list));
BUG_ON(!list_empty(&dev_priv->render_ring.request_list));
- BUG_ON(HAS_BSD(dev) && !list_empty(&dev_priv->bsd_ring.request_list));
+ BUG_ON(!list_empty(&dev_priv->bsd_ring.request_list));
+ BUG_ON(!list_empty(&dev_priv->blt_ring.request_list));
mutex_unlock(&dev->struct_mutex);
ret = drm_irq_install(dev);
@@ -4627,28 +4615,34 @@ i915_gem_lastclose(struct drm_device *dev)
DRM_ERROR("failed to idle hardware: %d\n", ret);
}
+static void
+init_ring_lists(struct intel_ring_buffer *ring)
+{
+ INIT_LIST_HEAD(&ring->active_list);
+ INIT_LIST_HEAD(&ring->request_list);
+ INIT_LIST_HEAD(&ring->gpu_write_list);
+}
+
void
i915_gem_load(struct drm_device *dev)
{
int i;
drm_i915_private_t *dev_priv = dev->dev_private;
- spin_lock_init(&dev_priv->mm.active_list_lock);
+ INIT_LIST_HEAD(&dev_priv->mm.active_list);
INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
- INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list);
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
+ INIT_LIST_HEAD(&dev_priv->mm.pinned_list);
INIT_LIST_HEAD(&dev_priv->mm.fence_list);
INIT_LIST_HEAD(&dev_priv->mm.deferred_free_list);
- INIT_LIST_HEAD(&dev_priv->render_ring.active_list);
- INIT_LIST_HEAD(&dev_priv->render_ring.request_list);
- if (HAS_BSD(dev)) {
- INIT_LIST_HEAD(&dev_priv->bsd_ring.active_list);
- INIT_LIST_HEAD(&dev_priv->bsd_ring.request_list);
- }
+ init_ring_lists(&dev_priv->render_ring);
+ init_ring_lists(&dev_priv->bsd_ring);
+ init_ring_lists(&dev_priv->blt_ring);
for (i = 0; i < 16; i++)
INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list);
INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
i915_gem_retire_work_handler);
+ init_completion(&dev_priv->error_completion);
spin_lock(&shrink_list_lock);
list_add(&dev_priv->mm.shrink_list, &shrink_list);
spin_unlock(&shrink_list_lock);
@@ -4667,21 +4661,30 @@ i915_gem_load(struct drm_device *dev)
if (!drm_core_check_feature(dev, DRIVER_MODESET))
dev_priv->fence_reg_start = 3;
- if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+ if (INTEL_INFO(dev)->gen >= 4 || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
dev_priv->num_fence_regs = 16;
else
dev_priv->num_fence_regs = 8;
/* Initialize fence registers to zero */
- if (IS_I965G(dev)) {
+ switch (INTEL_INFO(dev)->gen) {
+ case 6:
+ for (i = 0; i < 16; i++)
+ I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), 0);
+ break;
+ case 5:
+ case 4:
for (i = 0; i < 16; i++)
I915_WRITE64(FENCE_REG_965_0 + (i * 8), 0);
- } else {
- for (i = 0; i < 8; i++)
- I915_WRITE(FENCE_REG_830_0 + (i * 4), 0);
+ break;
+ case 3:
if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
for (i = 0; i < 8; i++)
I915_WRITE(FENCE_REG_945_8 + (i * 4), 0);
+ case 2:
+ for (i = 0; i < 8; i++)
+ I915_WRITE(FENCE_REG_830_0 + (i * 4), 0);
+ break;
}
i915_gem_detect_bit_6_swizzle(dev);
init_waitqueue_head(&dev_priv->pending_flip_queue);
@@ -4691,8 +4694,8 @@ i915_gem_load(struct drm_device *dev)
* Create a physically contiguous memory object for this object
* e.g. for cursor + overlay regs
*/
-int i915_gem_init_phys_object(struct drm_device *dev,
- int id, int size, int align)
+static int i915_gem_init_phys_object(struct drm_device *dev,
+ int id, int size, int align)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_phys_object *phys_obj;
@@ -4724,7 +4727,7 @@ kfree_obj:
return ret;
}
-void i915_gem_free_phys_object(struct drm_device *dev, int id)
+static void i915_gem_free_phys_object(struct drm_device *dev, int id)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_phys_object *phys_obj;
@@ -4772,11 +4775,11 @@ void i915_gem_detach_phys_object(struct drm_device *dev,
page_count = obj->size / PAGE_SIZE;
for (i = 0; i < page_count; i++) {
- char *dst = kmap_atomic(obj_priv->pages[i], KM_USER0);
+ char *dst = kmap_atomic(obj_priv->pages[i]);
char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
memcpy(dst, src, PAGE_SIZE);
- kunmap_atomic(dst, KM_USER0);
+ kunmap_atomic(dst);
}
drm_clflush_pages(obj_priv->pages, page_count);
drm_agp_chipset_flush(dev);
@@ -4833,11 +4836,11 @@ i915_gem_attach_phys_object(struct drm_device *dev,
page_count = obj->size / PAGE_SIZE;
for (i = 0; i < page_count; i++) {
- char *src = kmap_atomic(obj_priv->pages[i], KM_USER0);
+ char *src = kmap_atomic(obj_priv->pages[i]);
char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
memcpy(dst, src, PAGE_SIZE);
- kunmap_atomic(src, KM_USER0);
+ kunmap_atomic(src);
}
i915_gem_object_put_pages(obj);
@@ -4869,18 +4872,25 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
return 0;
}
-void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv)
+void i915_gem_release(struct drm_device *dev, struct drm_file *file)
{
- struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
/* Clean up our request list when the client is going away, so that
* later retire_requests won't dereference our soon-to-be-gone
* file_priv.
*/
- mutex_lock(&dev->struct_mutex);
- while (!list_empty(&i915_file_priv->mm.request_list))
- list_del_init(i915_file_priv->mm.request_list.next);
- mutex_unlock(&dev->struct_mutex);
+ spin_lock(&file_priv->mm.lock);
+ while (!list_empty(&file_priv->mm.request_list)) {
+ struct drm_i915_gem_request *request;
+
+ request = list_first_entry(&file_priv->mm.request_list,
+ struct drm_i915_gem_request,
+ client_list);
+ list_del(&request->client_list);
+ request->file_priv = NULL;
+ }
+ spin_unlock(&file_priv->mm.lock);
}
static int
@@ -4889,12 +4899,10 @@ i915_gpu_is_active(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
int lists_empty;
- spin_lock(&dev_priv->mm.active_list_lock);
lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
- list_empty(&dev_priv->render_ring.active_list);
- if (HAS_BSD(dev))
- lists_empty &= list_empty(&dev_priv->bsd_ring.active_list);
- spin_unlock(&dev_priv->mm.active_list_lock);
+ list_empty(&dev_priv->render_ring.active_list) &&
+ list_empty(&dev_priv->bsd_ring.active_list) &&
+ list_empty(&dev_priv->blt_ring.active_list);
return !lists_empty;
}
@@ -4916,7 +4924,7 @@ i915_gem_shrink(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask)
if (mutex_trylock(&dev->struct_mutex)) {
list_for_each_entry(obj_priv,
&dev_priv->mm.inactive_list,
- list)
+ mm_list)
cnt++;
mutex_unlock(&dev->struct_mutex);
}
@@ -4942,7 +4950,7 @@ rescan:
list_for_each_entry_safe(obj_priv, next_obj,
&dev_priv->mm.inactive_list,
- list) {
+ mm_list) {
if (i915_gem_object_is_purgeable(obj_priv)) {
i915_gem_object_unbind(&obj_priv->base);
if (--nr_to_scan <= 0)
@@ -4971,7 +4979,7 @@ rescan:
list_for_each_entry_safe(obj_priv, next_obj,
&dev_priv->mm.inactive_list,
- list) {
+ mm_list) {
if (nr_to_scan > 0) {
i915_gem_object_unbind(&obj_priv->base);
nr_to_scan--;
diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c
index 80f380b1d951..48644b840a8d 100644
--- a/drivers/gpu/drm/i915/i915_gem_debug.c
+++ b/drivers/gpu/drm/i915/i915_gem_debug.c
@@ -30,29 +30,112 @@
#include "i915_drm.h"
#include "i915_drv.h"
-#if WATCH_INACTIVE
-void
-i915_verify_inactive(struct drm_device *dev, char *file, int line)
+#if WATCH_LISTS
+int
+i915_verify_lists(struct drm_device *dev)
{
+ static int warned;
drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_gem_object *obj;
- struct drm_i915_gem_object *obj_priv;
-
- list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) {
- obj = &obj_priv->base;
- if (obj_priv->pin_count || obj_priv->active ||
- (obj->write_domain & ~(I915_GEM_DOMAIN_CPU |
- I915_GEM_DOMAIN_GTT)))
- DRM_ERROR("inactive %p (p %d a %d w %x) %s:%d\n",
+ struct drm_i915_gem_object *obj;
+ int err = 0;
+
+ if (warned)
+ return 0;
+
+ list_for_each_entry(obj, &dev_priv->render_ring.active_list, list) {
+ if (obj->base.dev != dev ||
+ !atomic_read(&obj->base.refcount.refcount)) {
+ DRM_ERROR("freed render active %p\n", obj);
+ err++;
+ break;
+ } else if (!obj->active ||
+ (obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0) {
+ DRM_ERROR("invalid render active %p (a %d r %x)\n",
+ obj,
+ obj->active,
+ obj->base.read_domains);
+ err++;
+ } else if (obj->base.write_domain && list_empty(&obj->gpu_write_list)) {
+ DRM_ERROR("invalid render active %p (w %x, gwl %d)\n",
+ obj,
+ obj->base.write_domain,
+ !list_empty(&obj->gpu_write_list));
+ err++;
+ }
+ }
+
+ list_for_each_entry(obj, &dev_priv->mm.flushing_list, list) {
+ if (obj->base.dev != dev ||
+ !atomic_read(&obj->base.refcount.refcount)) {
+ DRM_ERROR("freed flushing %p\n", obj);
+ err++;
+ break;
+ } else if (!obj->active ||
+ (obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0 ||
+ list_empty(&obj->gpu_write_list)){
+ DRM_ERROR("invalid flushing %p (a %d w %x gwl %d)\n",
obj,
- obj_priv->pin_count, obj_priv->active,
- obj->write_domain, file, line);
+ obj->active,
+ obj->base.write_domain,
+ !list_empty(&obj->gpu_write_list));
+ err++;
+ }
+ }
+
+ list_for_each_entry(obj, &dev_priv->mm.gpu_write_list, gpu_write_list) {
+ if (obj->base.dev != dev ||
+ !atomic_read(&obj->base.refcount.refcount)) {
+ DRM_ERROR("freed gpu write %p\n", obj);
+ err++;
+ break;
+ } else if (!obj->active ||
+ (obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0) {
+ DRM_ERROR("invalid gpu write %p (a %d w %x)\n",
+ obj,
+ obj->active,
+ obj->base.write_domain);
+ err++;
+ }
+ }
+
+ list_for_each_entry(obj, &dev_priv->mm.inactive_list, list) {
+ if (obj->base.dev != dev ||
+ !atomic_read(&obj->base.refcount.refcount)) {
+ DRM_ERROR("freed inactive %p\n", obj);
+ err++;
+ break;
+ } else if (obj->pin_count || obj->active ||
+ (obj->base.write_domain & I915_GEM_GPU_DOMAINS)) {
+ DRM_ERROR("invalid inactive %p (p %d a %d w %x)\n",
+ obj,
+ obj->pin_count, obj->active,
+ obj->base.write_domain);
+ err++;
+ }
}
+
+ list_for_each_entry(obj, &dev_priv->mm.pinned_list, list) {
+ if (obj->base.dev != dev ||
+ !atomic_read(&obj->base.refcount.refcount)) {
+ DRM_ERROR("freed pinned %p\n", obj);
+ err++;
+ break;
+ } else if (!obj->pin_count || obj->active ||
+ (obj->base.write_domain & I915_GEM_GPU_DOMAINS)) {
+ DRM_ERROR("invalid pinned %p (p %d a %d w %x)\n",
+ obj,
+ obj->pin_count, obj->active,
+ obj->base.write_domain);
+ err++;
+ }
+ }
+
+ return warned = err;
}
#endif /* WATCH_INACTIVE */
-#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE
+#if WATCH_EXEC | WATCH_PWRITE
static void
i915_gem_dump_page(struct page *page, uint32_t start, uint32_t end,
uint32_t bias, uint32_t mark)
@@ -97,41 +180,6 @@ i915_gem_dump_object(struct drm_gem_object *obj, int len,
}
#endif
-#if WATCH_LRU
-void
-i915_dump_lru(struct drm_device *dev, const char *where)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj_priv;
-
- DRM_INFO("active list %s {\n", where);
- spin_lock(&dev_priv->mm.active_list_lock);
- list_for_each_entry(obj_priv, &dev_priv->mm.active_list,
- list)
- {
- DRM_INFO(" %p: %08x\n", obj_priv,
- obj_priv->last_rendering_seqno);
- }
- spin_unlock(&dev_priv->mm.active_list_lock);
- DRM_INFO("}\n");
- DRM_INFO("flushing list %s {\n", where);
- list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list,
- list)
- {
- DRM_INFO(" %p: %08x\n", obj_priv,
- obj_priv->last_rendering_seqno);
- }
- DRM_INFO("}\n");
- DRM_INFO("inactive %s {\n", where);
- list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) {
- DRM_INFO(" %p: %08x\n", obj_priv,
- obj_priv->last_rendering_seqno);
- }
- DRM_INFO("}\n");
-}
-#endif
-
-
#if WATCH_COHERENCY
void
i915_gem_object_check_coherency(struct drm_gem_object *obj, int handle)
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 5c428fa3e0b3..43a4013f53fa 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -31,49 +31,6 @@
#include "i915_drv.h"
#include "i915_drm.h"
-static struct drm_i915_gem_object *
-i915_gem_next_active_object(struct drm_device *dev,
- struct list_head **render_iter,
- struct list_head **bsd_iter)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *render_obj = NULL, *bsd_obj = NULL;
-
- if (*render_iter != &dev_priv->render_ring.active_list)
- render_obj = list_entry(*render_iter,
- struct drm_i915_gem_object,
- list);
-
- if (HAS_BSD(dev)) {
- if (*bsd_iter != &dev_priv->bsd_ring.active_list)
- bsd_obj = list_entry(*bsd_iter,
- struct drm_i915_gem_object,
- list);
-
- if (render_obj == NULL) {
- *bsd_iter = (*bsd_iter)->next;
- return bsd_obj;
- }
-
- if (bsd_obj == NULL) {
- *render_iter = (*render_iter)->next;
- return render_obj;
- }
-
- /* XXX can we handle seqno wrapping? */
- if (render_obj->last_rendering_seqno < bsd_obj->last_rendering_seqno) {
- *render_iter = (*render_iter)->next;
- return render_obj;
- } else {
- *bsd_iter = (*bsd_iter)->next;
- return bsd_obj;
- }
- } else {
- *render_iter = (*render_iter)->next;
- return render_obj;
- }
-}
-
static bool
mark_free(struct drm_i915_gem_object *obj_priv,
struct list_head *unwind)
@@ -83,18 +40,12 @@ mark_free(struct drm_i915_gem_object *obj_priv,
return drm_mm_scan_add_block(obj_priv->gtt_space);
}
-#define i915_for_each_active_object(OBJ, R, B) \
- *(R) = dev_priv->render_ring.active_list.next; \
- *(B) = dev_priv->bsd_ring.active_list.next; \
- while (((OBJ) = i915_gem_next_active_object(dev, (R), (B))) != NULL)
-
int
i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct list_head eviction_list, unwind_list;
struct drm_i915_gem_object *obj_priv;
- struct list_head *render_iter, *bsd_iter;
int ret = 0;
i915_gem_retire_requests(dev);
@@ -131,13 +82,13 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignmen
drm_mm_init_scan(&dev_priv->mm.gtt_space, min_size, alignment);
/* First see if there is a large enough contiguous idle region... */
- list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, mm_list) {
if (mark_free(obj_priv, &unwind_list))
goto found;
}
/* Now merge in the soon-to-be-expired objects... */
- i915_for_each_active_object(obj_priv, &render_iter, &bsd_iter) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.active_list, mm_list) {
/* Does the object require an outstanding flush? */
if (obj_priv->base.write_domain || obj_priv->pin_count)
continue;
@@ -147,14 +98,14 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignmen
}
/* Finally add anything with a pending flush (in order of retirement) */
- list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, list) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, mm_list) {
if (obj_priv->pin_count)
continue;
if (mark_free(obj_priv, &unwind_list))
goto found;
}
- i915_for_each_active_object(obj_priv, &render_iter, &bsd_iter) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.active_list, mm_list) {
if (! obj_priv->base.write_domain || obj_priv->pin_count)
continue;
@@ -212,14 +163,11 @@ i915_gem_evict_everything(struct drm_device *dev)
int ret;
bool lists_empty;
- spin_lock(&dev_priv->mm.active_list_lock);
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->render_ring.active_list) &&
- (!HAS_BSD(dev)
- || list_empty(&dev_priv->bsd_ring.active_list)));
- spin_unlock(&dev_priv->mm.active_list_lock);
-
+ list_empty(&dev_priv->bsd_ring.active_list) &&
+ list_empty(&dev_priv->blt_ring.active_list));
if (lists_empty)
return -ENOSPC;
@@ -234,13 +182,11 @@ i915_gem_evict_everything(struct drm_device *dev)
if (ret)
return ret;
- spin_lock(&dev_priv->mm.active_list_lock);
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->render_ring.active_list) &&
- (!HAS_BSD(dev)
- || list_empty(&dev_priv->bsd_ring.active_list)));
- spin_unlock(&dev_priv->mm.active_list_lock);
+ list_empty(&dev_priv->bsd_ring.active_list) &&
+ list_empty(&dev_priv->blt_ring.active_list));
BUG_ON(!lists_empty);
return 0;
@@ -258,7 +204,7 @@ i915_gem_evict_inactive(struct drm_device *dev)
obj = &list_first_entry(&dev_priv->mm.inactive_list,
struct drm_i915_gem_object,
- list)->base;
+ mm_list)->base;
ret = i915_gem_object_unbind(obj);
if (ret != 0) {
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 710eca70b323..af352de70be1 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -92,13 +92,13 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
- if (IS_IRONLAKE(dev) || IS_GEN6(dev)) {
+ if (IS_GEN5(dev) || IS_GEN6(dev)) {
/* On Ironlake whatever DRAM config, GPU always do
* same swizzling setup.
*/
swizzle_x = I915_BIT_6_SWIZZLE_9_10;
swizzle_y = I915_BIT_6_SWIZZLE_9;
- } else if (!IS_I9XX(dev)) {
+ } else if (IS_GEN2(dev)) {
/* As far as we know, the 865 doesn't have these bit 6
* swizzling issues.
*/
@@ -190,19 +190,19 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
if (tiling_mode == I915_TILING_NONE)
return true;
- if (!IS_I9XX(dev) ||
+ if (IS_GEN2(dev) ||
(tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)))
tile_width = 128;
else
tile_width = 512;
/* check maximum stride & object size */
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
/* i965 stores the end address of the gtt mapping in the fence
* reg, so dont bother to check the size */
if (stride / 128 > I965_FENCE_MAX_PITCH_VAL)
return false;
- } else if (IS_GEN3(dev) || IS_GEN2(dev)) {
+ } else {
if (stride > 8192)
return false;
@@ -216,7 +216,7 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
}
/* 965+ just needs multiples of tile width */
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
if (stride & (tile_width - 1))
return false;
return true;
@@ -244,16 +244,18 @@ i915_gem_object_fence_offset_ok(struct drm_gem_object *obj, int tiling_mode)
if (tiling_mode == I915_TILING_NONE)
return true;
- if (!IS_I965G(dev)) {
- if (obj_priv->gtt_offset & (obj->size - 1))
+ if (INTEL_INFO(dev)->gen >= 4)
+ return true;
+
+ if (obj_priv->gtt_offset & (obj->size - 1))
+ return false;
+
+ if (IS_GEN3(dev)) {
+ if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK)
+ return false;
+ } else {
+ if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK)
return false;
- if (IS_I9XX(dev)) {
- if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK)
- return false;
- } else {
- if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK)
- return false;
- }
}
return true;
@@ -271,7 +273,11 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
- int ret = 0;
+ int ret;
+
+ ret = i915_gem_check_is_wedged(dev);
+ if (ret)
+ return ret;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
@@ -328,7 +334,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
if (!i915_gem_object_fence_offset_ok(obj, args->tiling_mode))
ret = i915_gem_object_unbind(obj);
else if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
- ret = i915_gem_object_put_fence_reg(obj);
+ ret = i915_gem_object_put_fence_reg(obj, true);
else
i915_gem_release_mmap(obj);
@@ -399,16 +405,14 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
* bit 17 of its physical address and therefore being interpreted differently
* by the GPU.
*/
-static int
+static void
i915_gem_swizzle_page(struct page *page)
{
+ char temp[64];
char *vaddr;
int i;
- char temp[64];
vaddr = kmap(page);
- if (vaddr == NULL)
- return -ENOMEM;
for (i = 0; i < PAGE_SIZE; i += 128) {
memcpy(temp, &vaddr[i], 64);
@@ -417,8 +421,6 @@ i915_gem_swizzle_page(struct page *page)
}
kunmap(page);
-
- return 0;
}
void
@@ -440,11 +442,7 @@ i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj)
char new_bit_17 = page_to_phys(obj_priv->pages[i]) >> 17;
if ((new_bit_17 & 0x1) !=
(test_bit(i, obj_priv->bit_17) != 0)) {
- int ret = i915_gem_swizzle_page(obj_priv->pages[i]);
- if (ret != 0) {
- DRM_ERROR("Failed to swizzle page\n");
- return;
- }
+ i915_gem_swizzle_page(obj_priv->pages[i]);
set_page_dirty(obj_priv->pages[i]);
}
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 744225ebb4b2..729fd0c91d7b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -85,7 +85,7 @@ ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
}
/* For display hotplug interrupt */
-void
+static void
ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
{
if ((dev_priv->irq_mask_reg & mask) != 0) {
@@ -172,7 +172,7 @@ void intel_enable_asle (struct drm_device *dev)
else {
i915_enable_pipestat(dev_priv, 1,
PIPE_LEGACY_BLC_EVENT_ENABLE);
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
i915_enable_pipestat(dev_priv, 0,
PIPE_LEGACY_BLC_EVENT_ENABLE);
}
@@ -191,12 +191,7 @@ static int
i915_pipe_enabled(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF;
-
- if (I915_READ(pipeconf) & PIPEACONF_ENABLE)
- return 1;
-
- return 0;
+ return I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE;
}
/* Called from drm generic code, passed a 'crtc', which
@@ -207,10 +202,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long high_frame;
unsigned long low_frame;
- u32 high1, high2, low, count;
-
- high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
- low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
+ u32 high1, high2, low;
if (!i915_pipe_enabled(dev, pipe)) {
DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
@@ -218,23 +210,23 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
return 0;
}
+ high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
+ low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
+
/*
* High & low register fields aren't synchronized, so make sure
* we get a low value that's stable across two reads of the high
* register.
*/
do {
- high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
- PIPE_FRAME_HIGH_SHIFT);
- low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
- PIPE_FRAME_LOW_SHIFT);
- high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
- PIPE_FRAME_HIGH_SHIFT);
+ high1 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK;
+ low = I915_READ(low_frame) & PIPE_FRAME_LOW_MASK;
+ high2 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK;
} while (high1 != high2);
- count = (high1 << 8) | low;
-
- return count;
+ high1 >>= PIPE_FRAME_HIGH_SHIFT;
+ low >>= PIPE_FRAME_LOW_SHIFT;
+ return (high1 << 8) | low;
}
u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
@@ -260,16 +252,12 @@ static void i915_hotplug_work_func(struct work_struct *work)
hotplug_work);
struct drm_device *dev = dev_priv->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
- struct drm_encoder *encoder;
-
- if (mode_config->num_encoder) {
- list_for_each_entry(encoder, &mode_config->encoder_list, head) {
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
-
- if (intel_encoder->hot_plug)
- (*intel_encoder->hot_plug) (intel_encoder);
- }
- }
+ struct intel_encoder *encoder;
+
+ list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+ if (encoder->hot_plug)
+ encoder->hot_plug(encoder);
+
/* Just fire off a uevent and let userspace tell us what to do */
drm_helper_hpd_irq_event(dev);
}
@@ -305,13 +293,30 @@ static void i915_handle_rps_change(struct drm_device *dev)
return;
}
-irqreturn_t ironlake_irq_handler(struct drm_device *dev)
+static void notify_ring(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 seqno = ring->get_seqno(dev, ring);
+ ring->irq_gem_seqno = seqno;
+ trace_i915_gem_request_complete(dev, seqno);
+ wake_up_all(&ring->irq_queue);
+ dev_priv->hangcheck_count = 0;
+ mod_timer(&dev_priv->hangcheck_timer,
+ jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
+}
+
+static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
int ret = IRQ_NONE;
u32 de_iir, gt_iir, de_ier, pch_iir;
+ u32 hotplug_mask;
struct drm_i915_master_private *master_priv;
- struct intel_ring_buffer *render_ring = &dev_priv->render_ring;
+ u32 bsd_usr_interrupt = GT_BSD_USER_INTERRUPT;
+
+ if (IS_GEN6(dev))
+ bsd_usr_interrupt = GT_GEN6_BSD_USER_INTERRUPT;
/* disable master interrupt before clearing iir */
de_ier = I915_READ(DEIER);
@@ -325,6 +330,11 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
if (de_iir == 0 && gt_iir == 0 && pch_iir == 0)
goto done;
+ if (HAS_PCH_CPT(dev))
+ hotplug_mask = SDE_HOTPLUG_MASK_CPT;
+ else
+ hotplug_mask = SDE_HOTPLUG_MASK;
+
ret = IRQ_HANDLED;
if (dev->primary->master) {
@@ -334,29 +344,24 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
READ_BREADCRUMB(dev_priv);
}
- if (gt_iir & GT_PIPE_NOTIFY) {
- u32 seqno = render_ring->get_gem_seqno(dev, render_ring);
- render_ring->irq_gem_seqno = seqno;
- trace_i915_gem_request_complete(dev, seqno);
- DRM_WAKEUP(&dev_priv->render_ring.irq_queue);
- dev_priv->hangcheck_count = 0;
- mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
- }
- if (gt_iir & GT_BSD_USER_INTERRUPT)
- DRM_WAKEUP(&dev_priv->bsd_ring.irq_queue);
-
+ if (gt_iir & GT_PIPE_NOTIFY)
+ notify_ring(dev, &dev_priv->render_ring);
+ if (gt_iir & bsd_usr_interrupt)
+ notify_ring(dev, &dev_priv->bsd_ring);
+ if (HAS_BLT(dev) && gt_iir & GT_BLT_USER_INTERRUPT)
+ notify_ring(dev, &dev_priv->blt_ring);
if (de_iir & DE_GSE)
- ironlake_opregion_gse_intr(dev);
+ intel_opregion_gse_intr(dev);
if (de_iir & DE_PLANEA_FLIP_DONE) {
intel_prepare_page_flip(dev, 0);
- intel_finish_page_flip(dev, 0);
+ intel_finish_page_flip_plane(dev, 0);
}
if (de_iir & DE_PLANEB_FLIP_DONE) {
intel_prepare_page_flip(dev, 1);
- intel_finish_page_flip(dev, 1);
+ intel_finish_page_flip_plane(dev, 1);
}
if (de_iir & DE_PIPEA_VBLANK)
@@ -366,10 +371,8 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
drm_handle_vblank(dev, 1);
/* check event from PCH */
- if ((de_iir & DE_PCH_EVENT) &&
- (pch_iir & SDE_HOTPLUG_MASK)) {
+ if ((de_iir & DE_PCH_EVENT) && (pch_iir & hotplug_mask))
queue_work(dev_priv->wq, &dev_priv->hotplug_work);
- }
if (de_iir & DE_PCU_EVENT) {
I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
@@ -404,23 +407,20 @@ static void i915_error_work_func(struct work_struct *work)
char *reset_event[] = { "RESET=1", NULL };
char *reset_done_event[] = { "ERROR=0", NULL };
- DRM_DEBUG_DRIVER("generating error event\n");
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
if (atomic_read(&dev_priv->mm.wedged)) {
- if (IS_I965G(dev)) {
- DRM_DEBUG_DRIVER("resetting chip\n");
- kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
- if (!i965_reset(dev, GDRST_RENDER)) {
- atomic_set(&dev_priv->mm.wedged, 0);
- kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
- }
- } else {
- DRM_DEBUG_DRIVER("reboot required\n");
+ DRM_DEBUG_DRIVER("resetting chip\n");
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
+ if (!i915_reset(dev, GRDOM_RENDER)) {
+ atomic_set(&dev_priv->mm.wedged, 0);
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
}
+ complete_all(&dev_priv->error_completion);
}
}
+#ifdef CONFIG_DEBUG_FS
static struct drm_i915_error_object *
i915_error_object_create(struct drm_device *dev,
struct drm_gem_object *src)
@@ -456,10 +456,9 @@ i915_error_object_create(struct drm_device *dev,
local_irq_save(flags);
s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
- reloc_offset,
- KM_IRQ0);
+ reloc_offset);
memcpy_fromio(d, s, PAGE_SIZE);
- io_mapping_unmap_atomic(s, KM_IRQ0);
+ io_mapping_unmap_atomic(s);
local_irq_restore(flags);
dst->pages[page] = d;
@@ -511,7 +510,7 @@ i915_get_bbaddr(struct drm_device *dev, u32 *ring)
if (IS_I830(dev) || IS_845G(dev))
cmd = MI_BATCH_BUFFER;
- else if (IS_I965G(dev))
+ else if (INTEL_INFO(dev)->gen >= 4)
cmd = (MI_BATCH_BUFFER_START | (2 << 6) |
MI_BATCH_NON_SECURE_I965);
else
@@ -584,13 +583,16 @@ static void i915_capture_error_state(struct drm_device *dev)
return;
}
- error->seqno = i915_get_gem_seqno(dev, &dev_priv->render_ring);
+ DRM_DEBUG_DRIVER("generating error event\n");
+
+ error->seqno =
+ dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring);
error->eir = I915_READ(EIR);
error->pgtbl_er = I915_READ(PGTBL_ER);
error->pipeastat = I915_READ(PIPEASTAT);
error->pipebstat = I915_READ(PIPEBSTAT);
error->instpm = I915_READ(INSTPM);
- if (!IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen < 4) {
error->ipeir = I915_READ(IPEIR);
error->ipehr = I915_READ(IPEHR);
error->instdone = I915_READ(INSTDONE);
@@ -612,9 +614,7 @@ static void i915_capture_error_state(struct drm_device *dev)
batchbuffer[0] = NULL;
batchbuffer[1] = NULL;
count = 0;
- list_for_each_entry(obj_priv,
- &dev_priv->render_ring.active_list, list) {
-
+ list_for_each_entry(obj_priv, &dev_priv->mm.active_list, mm_list) {
struct drm_gem_object *obj = &obj_priv->base;
if (batchbuffer[0] == NULL &&
@@ -631,7 +631,7 @@ static void i915_capture_error_state(struct drm_device *dev)
}
/* Scan the other lists for completeness for those bizarre errors. */
if (batchbuffer[0] == NULL || batchbuffer[1] == NULL) {
- list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, list) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, mm_list) {
struct drm_gem_object *obj = &obj_priv->base;
if (batchbuffer[0] == NULL &&
@@ -649,7 +649,7 @@ static void i915_capture_error_state(struct drm_device *dev)
}
}
if (batchbuffer[0] == NULL || batchbuffer[1] == NULL) {
- list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, mm_list) {
struct drm_gem_object *obj = &obj_priv->base;
if (batchbuffer[0] == NULL &&
@@ -668,7 +668,7 @@ static void i915_capture_error_state(struct drm_device *dev)
}
/* We need to copy these to an anonymous buffer as the simplest
- * method to avoid being overwritten by userpace.
+ * method to avoid being overwritten by userspace.
*/
error->batchbuffer[0] = i915_error_object_create(dev, batchbuffer[0]);
if (batchbuffer[1] != batchbuffer[0])
@@ -690,8 +690,7 @@ static void i915_capture_error_state(struct drm_device *dev)
if (error->active_bo) {
int i = 0;
- list_for_each_entry(obj_priv,
- &dev_priv->render_ring.active_list, list) {
+ list_for_each_entry(obj_priv, &dev_priv->mm.active_list, mm_list) {
struct drm_gem_object *obj = &obj_priv->base;
error->active_bo[i].size = obj->size;
@@ -744,6 +743,9 @@ void i915_destroy_error_state(struct drm_device *dev)
if (error)
i915_error_state_free(dev, error);
}
+#else
+#define i915_capture_error_state(x)
+#endif
static void i915_report_and_clear_eir(struct drm_device *dev)
{
@@ -785,7 +787,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
}
}
- if (IS_I9XX(dev)) {
+ if (!IS_GEN2(dev)) {
if (eir & I915_ERROR_PAGE_TABLE) {
u32 pgtbl_err = I915_READ(PGTBL_ER);
printk(KERN_ERR "page table error\n");
@@ -811,7 +813,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
printk(KERN_ERR "instruction error\n");
printk(KERN_ERR " INSTPM: 0x%08x\n",
I915_READ(INSTPM));
- if (!IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen < 4) {
u32 ipeir = I915_READ(IPEIR);
printk(KERN_ERR " IPEIR: 0x%08x\n",
@@ -876,12 +878,17 @@ static void i915_handle_error(struct drm_device *dev, bool wedged)
i915_report_and_clear_eir(dev);
if (wedged) {
+ INIT_COMPLETION(dev_priv->error_completion);
atomic_set(&dev_priv->mm.wedged, 1);
/*
* Wakeup waiting processes so they don't hang
*/
- DRM_WAKEUP(&dev_priv->render_ring.irq_queue);
+ wake_up_all(&dev_priv->render_ring.irq_queue);
+ if (HAS_BSD(dev))
+ wake_up_all(&dev_priv->bsd_ring.irq_queue);
+ if (HAS_BLT(dev))
+ wake_up_all(&dev_priv->blt_ring.irq_queue);
}
queue_work(dev_priv->wq, &dev_priv->error_work);
@@ -912,7 +919,7 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe)
/* Potential stall - if we see that the flip has happened, assume a missed interrupt */
obj_priv = to_intel_bo(work->pending_flip_obj);
- if(IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
int dspsurf = intel_crtc->plane == 0 ? DSPASURF : DSPBSURF;
stall_detected = I915_READ(dspsurf) == obj_priv->gtt_offset;
} else {
@@ -942,7 +949,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
unsigned long irqflags;
int irq_received;
int ret = IRQ_NONE;
- struct intel_ring_buffer *render_ring = &dev_priv->render_ring;
atomic_inc(&dev_priv->irq_received);
@@ -951,7 +957,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
iir = I915_READ(IIR);
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS;
else
vblank_status = PIPE_VBLANK_INTERRUPT_STATUS;
@@ -1019,18 +1025,10 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
READ_BREADCRUMB(dev_priv);
}
- if (iir & I915_USER_INTERRUPT) {
- u32 seqno =
- render_ring->get_gem_seqno(dev, render_ring);
- render_ring->irq_gem_seqno = seqno;
- trace_i915_gem_request_complete(dev, seqno);
- DRM_WAKEUP(&dev_priv->render_ring.irq_queue);
- dev_priv->hangcheck_count = 0;
- mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
- }
-
+ if (iir & I915_USER_INTERRUPT)
+ notify_ring(dev, &dev_priv->render_ring);
if (HAS_BSD(dev) && (iir & I915_BSD_USER_INTERRUPT))
- DRM_WAKEUP(&dev_priv->bsd_ring.irq_queue);
+ notify_ring(dev, &dev_priv->bsd_ring);
if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) {
intel_prepare_page_flip(dev, 0);
@@ -1065,7 +1063,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
if ((pipea_stats & PIPE_LEGACY_BLC_EVENT_STATUS) ||
(pipeb_stats & PIPE_LEGACY_BLC_EVENT_STATUS) ||
(iir & I915_ASLE_INTERRUPT))
- opregion_asle_intr(dev);
+ intel_opregion_asle_intr(dev);
/* With MSI, interrupts are only generated when iir
* transitions from zero to nonzero. If another bit got
@@ -1207,18 +1205,15 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags;
- int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
- u32 pipeconf;
- pipeconf = I915_READ(pipeconf_reg);
- if (!(pipeconf & PIPEACONF_ENABLE))
+ if (!i915_pipe_enabled(dev, pipe))
return -EINVAL;
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
if (HAS_PCH_SPLIT(dev))
ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
- else if (IS_I965G(dev))
+ else if (INTEL_INFO(dev)->gen >= 4)
i915_enable_pipestat(dev_priv, pipe,
PIPE_START_VBLANK_INTERRUPT_ENABLE);
else
@@ -1252,7 +1247,7 @@ void i915_enable_interrupt (struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
if (!HAS_PCH_SPLIT(dev))
- opregion_enable_asle(dev);
+ intel_opregion_enable_asle(dev);
dev_priv->irq_enabled = 1;
}
@@ -1311,7 +1306,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
return -EINVAL;
}
-struct drm_i915_gem_request *
+static struct drm_i915_gem_request *
i915_get_tail_request(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -1331,11 +1326,7 @@ void i915_hangcheck_elapsed(unsigned long data)
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t acthd, instdone, instdone1;
- /* No reset support on this chip yet. */
- if (IS_GEN6(dev))
- return;
-
- if (!IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen < 4) {
acthd = I915_READ(ACTHD);
instdone = I915_READ(INSTDONE);
instdone1 = 0;
@@ -1347,9 +1338,8 @@ void i915_hangcheck_elapsed(unsigned long data)
/* If all work is done then ACTHD clearly hasn't advanced. */
if (list_empty(&dev_priv->render_ring.request_list) ||
- i915_seqno_passed(i915_get_gem_seqno(dev,
- &dev_priv->render_ring),
- i915_get_tail_request(dev)->seqno)) {
+ i915_seqno_passed(dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring),
+ i915_get_tail_request(dev)->seqno)) {
bool missed_wakeup = false;
dev_priv->hangcheck_count = 0;
@@ -1357,13 +1347,19 @@ void i915_hangcheck_elapsed(unsigned long data)
/* Issue a wake-up to catch stuck h/w. */
if (dev_priv->render_ring.waiting_gem_seqno &&
waitqueue_active(&dev_priv->render_ring.irq_queue)) {
- DRM_WAKEUP(&dev_priv->render_ring.irq_queue);
+ wake_up_all(&dev_priv->render_ring.irq_queue);
missed_wakeup = true;
}
if (dev_priv->bsd_ring.waiting_gem_seqno &&
waitqueue_active(&dev_priv->bsd_ring.irq_queue)) {
- DRM_WAKEUP(&dev_priv->bsd_ring.irq_queue);
+ wake_up_all(&dev_priv->bsd_ring.irq_queue);
+ missed_wakeup = true;
+ }
+
+ if (dev_priv->blt_ring.waiting_gem_seqno &&
+ waitqueue_active(&dev_priv->blt_ring.irq_queue)) {
+ wake_up_all(&dev_priv->blt_ring.irq_queue);
missed_wakeup = true;
}
@@ -1377,6 +1373,21 @@ 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");
+
+ if (!IS_GEN2(dev)) {
+ /* Is the chip hanging on a WAIT_FOR_EVENT?
+ * If so we can simply poke the RB_WAIT bit
+ * and break the hang. This should work on
+ * all but the second generation chipsets.
+ */
+ u32 tmp = I915_READ(PRB0_CTL);
+ if (tmp & RING_WAIT) {
+ I915_WRITE(PRB0_CTL, tmp);
+ POSTING_READ(PRB0_CTL);
+ goto out;
+ }
+ }
+
i915_handle_error(dev, true);
return;
}
@@ -1388,8 +1399,10 @@ void i915_hangcheck_elapsed(unsigned long data)
dev_priv->last_instdone1 = instdone1;
}
+out:
/* Reset timer case chip hangs without another request being added */
- mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
+ mod_timer(&dev_priv->hangcheck_timer,
+ jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
}
/* drm_dma.h hooks
@@ -1424,8 +1437,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE;
u32 render_mask = GT_PIPE_NOTIFY | GT_BSD_USER_INTERRUPT;
- u32 hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG |
- SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG;
+ u32 hotplug_mask;
dev_priv->irq_mask_reg = ~display_mask;
dev_priv->de_irq_enable_reg = display_mask | DE_PIPEA_VBLANK | DE_PIPEB_VBLANK;
@@ -1436,20 +1448,35 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
I915_WRITE(DEIER, dev_priv->de_irq_enable_reg);
(void) I915_READ(DEIER);
- /* Gen6 only needs render pipe_control now */
- if (IS_GEN6(dev))
- render_mask = GT_PIPE_NOTIFY;
+ if (IS_GEN6(dev)) {
+ render_mask =
+ GT_PIPE_NOTIFY |
+ GT_GEN6_BSD_USER_INTERRUPT |
+ GT_BLT_USER_INTERRUPT;
+ }
dev_priv->gt_irq_mask_reg = ~render_mask;
dev_priv->gt_irq_enable_reg = render_mask;
I915_WRITE(GTIIR, I915_READ(GTIIR));
I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
- if (IS_GEN6(dev))
+ if (IS_GEN6(dev)) {
I915_WRITE(GEN6_RENDER_IMR, ~GEN6_RENDER_PIPE_CONTROL_NOTIFY_INTERRUPT);
+ I915_WRITE(GEN6_BSD_IMR, ~GEN6_BSD_IMR_USER_INTERRUPT);
+ I915_WRITE(GEN6_BLITTER_IMR, ~GEN6_BLITTER_USER_INTERRUPT);
+ }
+
I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
(void) I915_READ(GTIER);
+ if (HAS_PCH_CPT(dev)) {
+ hotplug_mask = SDE_CRT_HOTPLUG_CPT | SDE_PORTB_HOTPLUG_CPT |
+ SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT ;
+ } else {
+ hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG |
+ SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG;
+ }
+
dev_priv->pch_irq_mask_reg = ~hotplug_mask;
dev_priv->pch_irq_enable_reg = hotplug_mask;
@@ -1506,9 +1533,10 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
u32 error_mask;
DRM_INIT_WAITQUEUE(&dev_priv->render_ring.irq_queue);
-
if (HAS_BSD(dev))
DRM_INIT_WAITQUEUE(&dev_priv->bsd_ring.irq_queue);
+ if (HAS_BLT(dev))
+ DRM_INIT_WAITQUEUE(&dev_priv->blt_ring.irq_queue);
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
@@ -1578,7 +1606,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
}
- opregion_enable_asle(dev);
+ intel_opregion_enable_asle(dev);
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 4f5e15577e89..25ed911a3112 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -25,52 +25,16 @@
#ifndef _I915_REG_H_
#define _I915_REG_H_
+#define _PIPE(pipe, a, b) ((a) + (pipe)*((b)-(a)))
+
/*
* The Bridge device's PCI config space has information about the
* fb aperture size and the amount of pre-reserved memory.
+ * This is all handled in the intel-gtt.ko module. i915.ko only
+ * cares about the vga bit for the vga rbiter.
*/
#define INTEL_GMCH_CTRL 0x52
#define INTEL_GMCH_VGA_DISABLE (1 << 1)
-#define INTEL_GMCH_ENABLED 0x4
-#define INTEL_GMCH_MEM_MASK 0x1
-#define INTEL_GMCH_MEM_64M 0x1
-#define INTEL_GMCH_MEM_128M 0
-
-#define INTEL_GMCH_GMS_MASK (0xf << 4)
-#define INTEL_855_GMCH_GMS_DISABLED (0x0 << 4)
-#define INTEL_855_GMCH_GMS_STOLEN_1M (0x1 << 4)
-#define INTEL_855_GMCH_GMS_STOLEN_4M (0x2 << 4)
-#define INTEL_855_GMCH_GMS_STOLEN_8M (0x3 << 4)
-#define INTEL_855_GMCH_GMS_STOLEN_16M (0x4 << 4)
-#define INTEL_855_GMCH_GMS_STOLEN_32M (0x5 << 4)
-
-#define INTEL_915G_GMCH_GMS_STOLEN_48M (0x6 << 4)
-#define INTEL_915G_GMCH_GMS_STOLEN_64M (0x7 << 4)
-#define INTEL_GMCH_GMS_STOLEN_128M (0x8 << 4)
-#define INTEL_GMCH_GMS_STOLEN_256M (0x9 << 4)
-#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4)
-#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4)
-#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4)
-#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4)
-
-#define SNB_GMCH_CTRL 0x50
-#define SNB_GMCH_GMS_STOLEN_MASK 0xF8
-#define SNB_GMCH_GMS_STOLEN_32M (1 << 3)
-#define SNB_GMCH_GMS_STOLEN_64M (2 << 3)
-#define SNB_GMCH_GMS_STOLEN_96M (3 << 3)
-#define SNB_GMCH_GMS_STOLEN_128M (4 << 3)
-#define SNB_GMCH_GMS_STOLEN_160M (5 << 3)
-#define SNB_GMCH_GMS_STOLEN_192M (6 << 3)
-#define SNB_GMCH_GMS_STOLEN_224M (7 << 3)
-#define SNB_GMCH_GMS_STOLEN_256M (8 << 3)
-#define SNB_GMCH_GMS_STOLEN_288M (9 << 3)
-#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3)
-#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3)
-#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3)
-#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3)
-#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3)
-#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3)
-#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3)
/* PCI config space */
@@ -106,10 +70,13 @@
#define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0)
#define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0)
#define LBB 0xf4
-#define GDRST 0xc0
-#define GDRST_FULL (0<<2)
-#define GDRST_RENDER (1<<2)
-#define GDRST_MEDIA (3<<2)
+
+/* Graphics reset regs */
+#define I965_GDRST 0xc0 /* PCI config register */
+#define ILK_GDSR 0x2ca4 /* MCHBAR offset */
+#define GRDOM_FULL (0<<2)
+#define GRDOM_RENDER (1<<2)
+#define GRDOM_MEDIA (3<<2)
/* VGA stuff */
@@ -192,11 +159,11 @@
#define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
#define MI_STORE_DWORD_INDEX_SHIFT 2
#define MI_LOAD_REGISTER_IMM MI_INSTR(0x22, 1)
+#define MI_FLUSH_DW MI_INSTR(0x26, 2) /* for GEN6 */
#define MI_BATCH_BUFFER MI_INSTR(0x30, 1)
#define MI_BATCH_NON_SECURE (1)
#define MI_BATCH_NON_SECURE_I965 (1<<8)
#define MI_BATCH_BUFFER_START MI_INSTR(0x31, 0)
-
/*
* 3D instructions used by the kernel
*/
@@ -249,6 +216,16 @@
#define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */
#define PIPE_CONTROL_STALL_EN (1<<1) /* in addr word, Ironlake+ only */
+
+/*
+ * Reset registers
+ */
+#define DEBUG_RESET_I830 0x6070
+#define DEBUG_RESET_FULL (1<<7)
+#define DEBUG_RESET_RENDER (1<<8)
+#define DEBUG_RESET_DISPLAY (1<<9)
+
+
/*
* Fence registers
*/
@@ -283,6 +260,17 @@
#define PRB0_HEAD 0x02034
#define PRB0_START 0x02038
#define PRB0_CTL 0x0203c
+#define RENDER_RING_BASE 0x02000
+#define BSD_RING_BASE 0x04000
+#define GEN6_BSD_RING_BASE 0x12000
+#define BLT_RING_BASE 0x22000
+#define RING_TAIL(base) ((base)+0x30)
+#define RING_HEAD(base) ((base)+0x34)
+#define RING_START(base) ((base)+0x38)
+#define RING_CTL(base) ((base)+0x3c)
+#define RING_HWS_PGA(base) ((base)+0x80)
+#define RING_HWS_PGA_GEN6(base) ((base)+0x2080)
+#define RING_ACTHD(base) ((base)+0x74)
#define TAIL_ADDR 0x001FFFF8
#define HEAD_WRAP_COUNT 0xFFE00000
#define HEAD_WRAP_ONE 0x00200000
@@ -295,6 +283,8 @@
#define RING_VALID_MASK 0x00000001
#define RING_VALID 0x00000001
#define RING_INVALID 0x00000000
+#define RING_WAIT_I8XX (1<<0) /* gen2, PRBx_HEAD */
+#define RING_WAIT (1<<11) /* gen3+, PRBx_CTL */
#define PRB1_TAIL 0x02040 /* 915+ only */
#define PRB1_HEAD 0x02044 /* 915+ only */
#define PRB1_START 0x02048 /* 915+ only */
@@ -306,7 +296,6 @@
#define INSTDONE1 0x0207c /* 965+ only */
#define ACTHD_I965 0x02074
#define HWS_PGA 0x02080
-#define HWS_PGA_GEN6 0x04080
#define HWS_ADDRESS_MASK 0xfffff000
#define HWS_START_ADDRESS_SHIFT 4
#define PWRCTXA 0x2088 /* 965GM+ only */
@@ -464,17 +453,17 @@
#define GEN6_BLITTER_COMMAND_PARSER_MASTER_ERROR (1 << 25)
#define GEN6_BLITTER_SYNC_STATUS (1 << 24)
#define GEN6_BLITTER_USER_INTERRUPT (1 << 22)
-/*
- * BSD (bit stream decoder instruction and interrupt control register defines
- * (G4X and Ironlake only)
- */
-#define BSD_RING_TAIL 0x04030
-#define BSD_RING_HEAD 0x04034
-#define BSD_RING_START 0x04038
-#define BSD_RING_CTL 0x0403c
-#define BSD_RING_ACTHD 0x04074
-#define BSD_HWS_PGA 0x04080
+#define GEN6_BSD_SLEEP_PSMI_CONTROL 0x12050
+#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK (1 << 16)
+#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE (1 << 0)
+#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE 0
+#define GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR (1 << 3)
+
+#define GEN6_BSD_IMR 0x120a8
+#define GEN6_BSD_IMR_USER_INTERRUPT (1 << 12)
+
+#define GEN6_BSD_RNCID 0x12198
/*
* Framebuffer compression (915+ only)
@@ -579,12 +568,51 @@
# define GPIO_DATA_VAL_IN (1 << 12)
# define GPIO_DATA_PULLUP_DISABLE (1 << 13)
-#define GMBUS0 0x5100
-#define GMBUS1 0x5104
-#define GMBUS2 0x5108
-#define GMBUS3 0x510c
-#define GMBUS4 0x5110
-#define GMBUS5 0x5120
+#define GMBUS0 0x5100 /* clock/port select */
+#define GMBUS_RATE_100KHZ (0<<8)
+#define GMBUS_RATE_50KHZ (1<<8)
+#define GMBUS_RATE_400KHZ (2<<8) /* reserved on Pineview */
+#define GMBUS_RATE_1MHZ (3<<8) /* reserved on Pineview */
+#define GMBUS_HOLD_EXT (1<<7) /* 300ns hold time, rsvd on Pineview */
+#define GMBUS_PORT_DISABLED 0
+#define GMBUS_PORT_SSC 1
+#define GMBUS_PORT_VGADDC 2
+#define GMBUS_PORT_PANEL 3
+#define GMBUS_PORT_DPC 4 /* HDMIC */
+#define GMBUS_PORT_DPB 5 /* SDVO, HDMIB */
+ /* 6 reserved */
+#define GMBUS_PORT_DPD 7 /* HDMID */
+#define GMBUS_NUM_PORTS 8
+#define GMBUS1 0x5104 /* command/status */
+#define GMBUS_SW_CLR_INT (1<<31)
+#define GMBUS_SW_RDY (1<<30)
+#define GMBUS_ENT (1<<29) /* enable timeout */
+#define GMBUS_CYCLE_NONE (0<<25)
+#define GMBUS_CYCLE_WAIT (1<<25)
+#define GMBUS_CYCLE_INDEX (2<<25)
+#define GMBUS_CYCLE_STOP (4<<25)
+#define GMBUS_BYTE_COUNT_SHIFT 16
+#define GMBUS_SLAVE_INDEX_SHIFT 8
+#define GMBUS_SLAVE_ADDR_SHIFT 1
+#define GMBUS_SLAVE_READ (1<<0)
+#define GMBUS_SLAVE_WRITE (0<<0)
+#define GMBUS2 0x5108 /* status */
+#define GMBUS_INUSE (1<<15)
+#define GMBUS_HW_WAIT_PHASE (1<<14)
+#define GMBUS_STALL_TIMEOUT (1<<13)
+#define GMBUS_INT (1<<12)
+#define GMBUS_HW_RDY (1<<11)
+#define GMBUS_SATOER (1<<10)
+#define GMBUS_ACTIVE (1<<9)
+#define GMBUS3 0x510c /* data buffer bytes 3-0 */
+#define GMBUS4 0x5110 /* interrupt mask (Pineview+) */
+#define GMBUS_SLAVE_TIMEOUT_EN (1<<4)
+#define GMBUS_NAK_EN (1<<3)
+#define GMBUS_IDLE_EN (1<<2)
+#define GMBUS_HW_WAIT_EN (1<<1)
+#define GMBUS_HW_RDY_EN (1<<0)
+#define GMBUS5 0x5120 /* byte index */
+#define GMBUS_2BYTE_INDEX_EN (1<<31)
/*
* Clock control & power management
@@ -603,6 +631,7 @@
#define VGA1_PD_P1_MASK (0x1f << 8)
#define DPLL_A 0x06014
#define DPLL_B 0x06018
+#define DPLL(pipe) _PIPE(pipe, DPLL_A, DPLL_B)
#define DPLL_VCO_ENABLE (1 << 31)
#define DPLL_DVO_HIGH_SPEED (1 << 30)
#define DPLL_SYNCLOCK_ENABLE (1 << 29)
@@ -633,31 +662,6 @@
#define LVDS 0x61180
#define LVDS_ON (1<<31)
-#define ADPA 0x61100
-#define ADPA_DPMS_MASK (~(3<<10))
-#define ADPA_DPMS_ON (0<<10)
-#define ADPA_DPMS_SUSPEND (1<<10)
-#define ADPA_DPMS_STANDBY (2<<10)
-#define ADPA_DPMS_OFF (3<<10)
-
-#define RING_TAIL 0x00
-#define TAIL_ADDR 0x001FFFF8
-#define RING_HEAD 0x04
-#define HEAD_WRAP_COUNT 0xFFE00000
-#define HEAD_WRAP_ONE 0x00200000
-#define HEAD_ADDR 0x001FFFFC
-#define RING_START 0x08
-#define START_ADDR 0xFFFFF000
-#define RING_LEN 0x0C
-#define RING_NR_PAGES 0x001FF000
-#define RING_REPORT_MASK 0x00000006
-#define RING_REPORT_64K 0x00000002
-#define RING_REPORT_128K 0x00000004
-#define RING_NO_REPORT 0x00000000
-#define RING_VALID_MASK 0x00000001
-#define RING_VALID 0x00000001
-#define RING_INVALID 0x00000000
-
/* Scratch pad debug 0 reg:
*/
#define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000
@@ -736,10 +740,13 @@
#define DPLL_MD_VGA_UDI_MULTIPLIER_MASK 0x0000003f
#define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0
#define DPLL_B_MD 0x06020 /* 965+ only */
+#define DPLL_MD(pipe) _PIPE(pipe, DPLL_A_MD, DPLL_B_MD)
#define FPA0 0x06040
#define FPA1 0x06044
#define FPB0 0x06048
#define FPB1 0x0604c
+#define FP0(pipe) _PIPE(pipe, FPA0, FPB0)
+#define FP1(pipe) _PIPE(pipe, FPA1, FPB1)
#define FP_N_DIV_MASK 0x003f0000
#define FP_N_PINEVIEW_DIV_MASK 0x00ff0000
#define FP_N_DIV_SHIFT 16
@@ -760,6 +767,7 @@
#define DPLLA_TEST_M_BYPASS (1 << 2)
#define DPLLA_INPUT_BUFFER_ENABLE (1 << 0)
#define D_STATE 0x6104
+#define DSTATE_GFX_RESET_I830 (1<<6)
#define DSTATE_PLL_D3_OFF (1<<3)
#define DSTATE_GFX_CLOCK_GATING (1<<1)
#define DSTATE_DOT_CLOCK_GATING (1<<0)
@@ -926,6 +934,8 @@
#define CLKCFG_MEM_800 (3 << 4)
#define CLKCFG_MEM_MASK (7 << 4)
+#define TSC1 0x11001
+#define TSE (1<<0)
#define TR1 0x11006
#define TSFS 0x11020
#define TSFS_SLOPE_MASK 0x0000ff00
@@ -1070,6 +1080,8 @@
#define MEMSTAT_SRC_CTL_STDBY 3
#define RCPREVBSYTUPAVG 0x113b8
#define RCPREVBSYTDNAVG 0x113bc
+#define PMMISC 0x11214
+#define MCPPCE_EN (1<<0) /* enable PM_MSG from PCH->MPC */
#define SDEW 0x1124c
#define CSIEW0 0x11250
#define CSIEW1 0x11254
@@ -1150,6 +1162,15 @@
#define PIPEBSRC 0x6101c
#define BCLRPAT_B 0x61020
+#define HTOTAL(pipe) _PIPE(pipe, HTOTAL_A, HTOTAL_B)
+#define HBLANK(pipe) _PIPE(pipe, HBLANK_A, HBLANK_B)
+#define HSYNC(pipe) _PIPE(pipe, HSYNC_A, HSYNC_B)
+#define VTOTAL(pipe) _PIPE(pipe, VTOTAL_A, VTOTAL_B)
+#define VBLANK(pipe) _PIPE(pipe, VBLANK_A, VBLANK_B)
+#define VSYNC(pipe) _PIPE(pipe, VSYNC_A, VSYNC_B)
+#define PIPESRC(pipe) _PIPE(pipe, PIPEASRC, PIPEBSRC)
+#define BCLRPAT(pipe) _PIPE(pipe, BCLRPAT_A, BCLRPAT_B)
+
/* VGA port control */
#define ADPA 0x61100
#define ADPA_DAC_ENABLE (1<<31)
@@ -1173,6 +1194,7 @@
#define ADPA_DPMS_STANDBY (2<<10)
#define ADPA_DPMS_OFF (3<<10)
+
/* Hotplug control (945+ only) */
#define PORT_HOTPLUG_EN 0x61110
#define HDMIB_HOTPLUG_INT_EN (1 << 29)
@@ -1331,6 +1353,22 @@
#define LVDS_B0B3_POWER_DOWN (0 << 2)
#define LVDS_B0B3_POWER_UP (3 << 2)
+/* Video Data Island Packet control */
+#define VIDEO_DIP_DATA 0x61178
+#define VIDEO_DIP_CTL 0x61170
+#define VIDEO_DIP_ENABLE (1 << 31)
+#define VIDEO_DIP_PORT_B (1 << 29)
+#define VIDEO_DIP_PORT_C (2 << 29)
+#define VIDEO_DIP_ENABLE_AVI (1 << 21)
+#define VIDEO_DIP_ENABLE_VENDOR (2 << 21)
+#define VIDEO_DIP_ENABLE_SPD (8 << 21)
+#define VIDEO_DIP_SELECT_AVI (0 << 19)
+#define VIDEO_DIP_SELECT_VENDOR (1 << 19)
+#define VIDEO_DIP_SELECT_SPD (3 << 19)
+#define VIDEO_DIP_FREQ_ONCE (0 << 16)
+#define VIDEO_DIP_FREQ_VSYNC (1 << 16)
+#define VIDEO_DIP_FREQ_2VSYNC (2 << 16)
+
/* Panel power sequencing */
#define PP_STATUS 0x61200
#define PP_ON (1 << 31)
@@ -1346,6 +1384,9 @@
#define PP_SEQUENCE_ON (1 << 28)
#define PP_SEQUENCE_OFF (2 << 28)
#define PP_SEQUENCE_MASK 0x30000000
+#define PP_CYCLE_DELAY_ACTIVE (1 << 27)
+#define PP_SEQUENCE_STATE_ON_IDLE (1 << 3)
+#define PP_SEQUENCE_STATE_MASK 0x0000000f
#define PP_CONTROL 0x61204
#define POWER_TARGET_ON (1 << 0)
#define PP_ON_DELAYS 0x61208
@@ -1481,6 +1522,7 @@
# define TV_TEST_MODE_MASK (7 << 0)
#define TV_DAC 0x68004
+# define TV_DAC_SAVE 0x00ffff00
/**
* Reports that DAC state change logic has reported change (RO).
*
@@ -2075,29 +2117,35 @@
/* Display & cursor control */
-/* dithering flag on Ironlake */
-#define PIPE_ENABLE_DITHER (1 << 4)
-#define PIPE_DITHER_TYPE_MASK (3 << 2)
-#define PIPE_DITHER_TYPE_SPATIAL (0 << 2)
-#define PIPE_DITHER_TYPE_ST01 (1 << 2)
/* Pipe A */
#define PIPEADSL 0x70000
-#define DSL_LINEMASK 0x00000fff
+#define DSL_LINEMASK 0x00000fff
#define PIPEACONF 0x70008
-#define PIPEACONF_ENABLE (1<<31)
-#define PIPEACONF_DISABLE 0
-#define PIPEACONF_DOUBLE_WIDE (1<<30)
+#define PIPECONF_ENABLE (1<<31)
+#define PIPECONF_DISABLE 0
+#define PIPECONF_DOUBLE_WIDE (1<<30)
#define I965_PIPECONF_ACTIVE (1<<30)
-#define PIPEACONF_SINGLE_WIDE 0
-#define PIPEACONF_PIPE_UNLOCKED 0
-#define PIPEACONF_PIPE_LOCKED (1<<25)
-#define PIPEACONF_PALETTE 0
-#define PIPEACONF_GAMMA (1<<24)
+#define PIPECONF_SINGLE_WIDE 0
+#define PIPECONF_PIPE_UNLOCKED 0
+#define PIPECONF_PIPE_LOCKED (1<<25)
+#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_CXSR_DOWNCLOCK (1<<16)
+#define PIPECONF_BPP_MASK (0x000000e0)
+#define PIPECONF_BPP_8 (0<<5)
+#define PIPECONF_BPP_10 (1<<5)
+#define PIPECONF_BPP_6 (2<<5)
+#define PIPECONF_BPP_12 (3<<5)
+#define PIPECONF_DITHER_EN (1<<4)
+#define PIPECONF_DITHER_TYPE_MASK (0x0000000c)
+#define PIPECONF_DITHER_TYPE_SP (0<<2)
+#define PIPECONF_DITHER_TYPE_ST1 (1<<2)
+#define PIPECONF_DITHER_TYPE_ST2 (2<<2)
+#define PIPECONF_DITHER_TYPE_TEMP (3<<2)
#define PIPEASTAT 0x70024
#define PIPE_FIFO_UNDERRUN_STATUS (1UL<<31)
#define PIPE_CRC_ERROR_ENABLE (1UL<<29)
@@ -2128,12 +2176,15 @@
#define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */
#define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1)
#define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0)
-#define PIPE_BPC_MASK (7 << 5) /* Ironlake */
+#define PIPE_BPC_MASK (7 << 5) /* Ironlake */
#define PIPE_8BPC (0 << 5)
#define PIPE_10BPC (1 << 5)
#define PIPE_6BPC (2 << 5)
#define PIPE_12BPC (3 << 5)
+#define PIPECONF(pipe) _PIPE(pipe, PIPEACONF, PIPEBCONF)
+#define PIPEDSL(pipe) _PIPE(pipe, PIPEADSL, PIPEBDSL)
+
#define DSPARB 0x70030
#define DSPARB_CSTART_MASK (0x7f << 7)
#define DSPARB_CSTART_SHIFT 7
@@ -2206,8 +2257,8 @@
#define WM1_LP_SR_EN (1<<31)
#define WM1_LP_LATENCY_SHIFT 24
#define WM1_LP_LATENCY_MASK (0x7f<<24)
-#define WM1_LP_FBC_LP1_MASK (0xf<<20)
-#define WM1_LP_FBC_LP1_SHIFT 20
+#define WM1_LP_FBC_MASK (0xf<<20)
+#define WM1_LP_FBC_SHIFT 20
#define WM1_LP_SR_MASK (0x1ff<<8)
#define WM1_LP_SR_SHIFT 8
#define WM1_LP_CURSOR_MASK (0x3f)
@@ -2333,6 +2384,14 @@
#define DSPASURF 0x7019C /* 965+ only */
#define DSPATILEOFF 0x701A4 /* 965+ only */
+#define DSPCNTR(plane) _PIPE(plane, DSPACNTR, DSPBCNTR)
+#define DSPADDR(plane) _PIPE(plane, DSPAADDR, DSPBADDR)
+#define DSPSTRIDE(plane) _PIPE(plane, DSPASTRIDE, DSPBSTRIDE)
+#define DSPPOS(plane) _PIPE(plane, DSPAPOS, DSPBPOS)
+#define DSPSIZE(plane) _PIPE(plane, DSPASIZE, DSPBSIZE)
+#define DSPSURF(plane) _PIPE(plane, DSPASURF, DSPBSURF)
+#define DSPTILEOFF(plane) _PIPE(plane, DSPATILEOFF, DSPBTILEOFF)
+
/* VBIOS flags */
#define SWF00 0x71410
#define SWF01 0x71414
@@ -2397,6 +2456,7 @@
#define RR_HW_HIGH_POWER_FRAMES_MASK 0xff00
#define FDI_PLL_BIOS_0 0x46000
+#define FDI_PLL_FB_CLOCK_MASK 0xff
#define FDI_PLL_BIOS_1 0x46004
#define FDI_PLL_BIOS_2 0x46008
#define DISPLAY_PORT_PLL_BIOS_0 0x4600c
@@ -2420,46 +2480,47 @@
#define PIPEA_DATA_M1 0x60030
#define TU_SIZE(x) (((x)-1) << 25) /* default size 64 */
#define TU_SIZE_MASK 0x7e000000
-#define PIPEA_DATA_M1_OFFSET 0
+#define PIPE_DATA_M1_OFFSET 0
#define PIPEA_DATA_N1 0x60034
-#define PIPEA_DATA_N1_OFFSET 0
+#define PIPE_DATA_N1_OFFSET 0
#define PIPEA_DATA_M2 0x60038
-#define PIPEA_DATA_M2_OFFSET 0
+#define PIPE_DATA_M2_OFFSET 0
#define PIPEA_DATA_N2 0x6003c
-#define PIPEA_DATA_N2_OFFSET 0
+#define PIPE_DATA_N2_OFFSET 0
#define PIPEA_LINK_M1 0x60040
-#define PIPEA_LINK_M1_OFFSET 0
+#define PIPE_LINK_M1_OFFSET 0
#define PIPEA_LINK_N1 0x60044
-#define PIPEA_LINK_N1_OFFSET 0
+#define PIPE_LINK_N1_OFFSET 0
#define PIPEA_LINK_M2 0x60048
-#define PIPEA_LINK_M2_OFFSET 0
+#define PIPE_LINK_M2_OFFSET 0
#define PIPEA_LINK_N2 0x6004c
-#define PIPEA_LINK_N2_OFFSET 0
+#define PIPE_LINK_N2_OFFSET 0
/* PIPEB timing regs are same start from 0x61000 */
#define PIPEB_DATA_M1 0x61030
-#define PIPEB_DATA_M1_OFFSET 0
#define PIPEB_DATA_N1 0x61034
-#define PIPEB_DATA_N1_OFFSET 0
#define PIPEB_DATA_M2 0x61038
-#define PIPEB_DATA_M2_OFFSET 0
#define PIPEB_DATA_N2 0x6103c
-#define PIPEB_DATA_N2_OFFSET 0
#define PIPEB_LINK_M1 0x61040
-#define PIPEB_LINK_M1_OFFSET 0
#define PIPEB_LINK_N1 0x61044
-#define PIPEB_LINK_N1_OFFSET 0
#define PIPEB_LINK_M2 0x61048
-#define PIPEB_LINK_M2_OFFSET 0
#define PIPEB_LINK_N2 0x6104c
-#define PIPEB_LINK_N2_OFFSET 0
+
+#define PIPE_DATA_M1(pipe) _PIPE(pipe, PIPEA_DATA_M1, PIPEB_DATA_M1)
+#define PIPE_DATA_N1(pipe) _PIPE(pipe, PIPEA_DATA_N1, PIPEB_DATA_N1)
+#define PIPE_DATA_M2(pipe) _PIPE(pipe, PIPEA_DATA_M2, PIPEB_DATA_M2)
+#define PIPE_DATA_N2(pipe) _PIPE(pipe, PIPEA_DATA_N2, PIPEB_DATA_N2)
+#define PIPE_LINK_M1(pipe) _PIPE(pipe, PIPEA_LINK_M1, PIPEB_LINK_M1)
+#define PIPE_LINK_N1(pipe) _PIPE(pipe, PIPEA_LINK_N1, PIPEB_LINK_N1)
+#define PIPE_LINK_M2(pipe) _PIPE(pipe, PIPEA_LINK_M2, PIPEB_LINK_M2)
+#define PIPE_LINK_N2(pipe) _PIPE(pipe, PIPEA_LINK_N2, PIPEB_LINK_N2)
/* CPU panel fitter */
#define PFA_CTL_1 0x68080
@@ -2516,7 +2577,8 @@
#define GT_SYNC_STATUS (1 << 2)
#define GT_USER_INTERRUPT (1 << 0)
#define GT_BSD_USER_INTERRUPT (1 << 5)
-
+#define GT_GEN6_BSD_USER_INTERRUPT (1 << 12)
+#define GT_BLT_USER_INTERRUPT (1 << 22)
#define GTISR 0x44010
#define GTIMR 0x44014
@@ -2551,6 +2613,10 @@
#define SDE_PORTD_HOTPLUG_CPT (1 << 23)
#define SDE_PORTC_HOTPLUG_CPT (1 << 22)
#define SDE_PORTB_HOTPLUG_CPT (1 << 21)
+#define SDE_HOTPLUG_MASK_CPT (SDE_CRT_HOTPLUG_CPT | \
+ SDE_PORTD_HOTPLUG_CPT | \
+ SDE_PORTC_HOTPLUG_CPT | \
+ SDE_PORTB_HOTPLUG_CPT)
#define SDEISR 0xc4000
#define SDEIMR 0xc4004
@@ -2600,11 +2666,14 @@
#define PCH_DPLL_A 0xc6014
#define PCH_DPLL_B 0xc6018
+#define PCH_DPLL(pipe) _PIPE(pipe, PCH_DPLL_A, PCH_DPLL_B)
#define PCH_FPA0 0xc6040
#define PCH_FPA1 0xc6044
#define PCH_FPB0 0xc6048
#define PCH_FPB1 0xc604c
+#define PCH_FP0(pipe) _PIPE(pipe, PCH_FPA0, PCH_FPB0)
+#define PCH_FP1(pipe) _PIPE(pipe, PCH_FPA1, PCH_FPB1)
#define PCH_DPLL_TEST 0xc606c
@@ -2690,6 +2759,13 @@
#define TRANS_VBLANK_B 0xe1010
#define TRANS_VSYNC_B 0xe1014
+#define TRANS_HTOTAL(pipe) _PIPE(pipe, TRANS_HTOTAL_A, TRANS_HTOTAL_B)
+#define TRANS_HBLANK(pipe) _PIPE(pipe, TRANS_HBLANK_A, TRANS_HBLANK_B)
+#define TRANS_HSYNC(pipe) _PIPE(pipe, TRANS_HSYNC_A, TRANS_HSYNC_B)
+#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 TRANSB_DATA_M1 0xe1030
#define TRANSB_DATA_N1 0xe1034
#define TRANSB_DATA_M2 0xe1038
@@ -2701,6 +2777,7 @@
#define TRANSACONF 0xf0008
#define TRANSBCONF 0xf1008
+#define TRANSCONF(plane) _PIPE(plane, TRANSACONF, TRANSBCONF)
#define TRANS_DISABLE (0<<31)
#define TRANS_ENABLE (1<<31)
#define TRANS_STATE_MASK (1<<30)
@@ -2721,10 +2798,15 @@
#define FDI_RXA_CHICKEN 0xc200c
#define FDI_RXB_CHICKEN 0xc2010
#define FDI_RX_PHASE_SYNC_POINTER_ENABLE (1)
+#define FDI_RX_CHICKEN(pipe) _PIPE(pipe, FDI_RXA_CHICKEN, FDI_RXB_CHICKEN)
+
+#define SOUTH_DSPCLK_GATE_D 0xc2020
+#define PCH_DPLSUNIT_CLOCK_GATE_DISABLE (1<<29)
/* CPU: FDI_TX */
#define FDI_TXA_CTL 0x60100
#define FDI_TXB_CTL 0x61100
+#define FDI_TX_CTL(pipe) _PIPE(pipe, FDI_TXA_CTL, FDI_TXB_CTL)
#define FDI_TX_DISABLE (0<<31)
#define FDI_TX_ENABLE (1<<31)
#define FDI_LINK_TRAIN_PATTERN_1 (0<<28)
@@ -2766,8 +2848,8 @@
/* FDI_RX, FDI_X is hard-wired to Transcoder_X */
#define FDI_RXA_CTL 0xf000c
#define FDI_RXB_CTL 0xf100c
+#define FDI_RX_CTL(pipe) _PIPE(pipe, FDI_RXA_CTL, FDI_RXB_CTL)
#define FDI_RX_ENABLE (1<<31)
-#define FDI_RX_DISABLE (0<<31)
/* train, dp width same as FDI_TX */
#define FDI_DP_PORT_WIDTH_X8 (7<<19)
#define FDI_8BPC (0<<16)
@@ -2782,8 +2864,7 @@
#define FDI_FS_ERR_REPORT_ENABLE (1<<9)
#define FDI_FE_ERR_REPORT_ENABLE (1<<8)
#define FDI_RX_ENHANCE_FRAME_ENABLE (1<<6)
-#define FDI_SEL_RAWCLK (0<<4)
-#define FDI_SEL_PCDCLK (1<<4)
+#define FDI_PCDCLK (1<<4)
/* CPT */
#define FDI_AUTO_TRAINING (1<<10)
#define FDI_LINK_TRAIN_PATTERN_1_CPT (0<<8)
@@ -2798,6 +2879,9 @@
#define FDI_RXA_TUSIZE2 0xf0038
#define FDI_RXB_TUSIZE1 0xf1030
#define FDI_RXB_TUSIZE2 0xf1038
+#define FDI_RX_MISC(pipe) _PIPE(pipe, FDI_RXA_MISC, FDI_RXB_MISC)
+#define FDI_RX_TUSIZE1(pipe) _PIPE(pipe, FDI_RXA_TUSIZE1, FDI_RXB_TUSIZE1)
+#define FDI_RX_TUSIZE2(pipe) _PIPE(pipe, FDI_RXA_TUSIZE2, FDI_RXB_TUSIZE2)
/* FDI_RX interrupt register format */
#define FDI_RX_INTER_LANE_ALIGN (1<<10)
@@ -2816,6 +2900,8 @@
#define FDI_RXA_IMR 0xf0018
#define FDI_RXB_IIR 0xf1014
#define FDI_RXB_IMR 0xf1018
+#define FDI_RX_IIR(pipe) _PIPE(pipe, FDI_RXA_IIR, FDI_RXB_IIR)
+#define FDI_RX_IMR(pipe) _PIPE(pipe, FDI_RXA_IMR, FDI_RXB_IMR)
#define FDI_PLL_CTL_1 0xfe000
#define FDI_PLL_CTL_2 0xfe004
@@ -2935,6 +3021,7 @@
#define TRANS_DP_CTL_A 0xe0300
#define TRANS_DP_CTL_B 0xe1300
#define TRANS_DP_CTL_C 0xe2300
+#define TRANS_DP_CTL(pipe) (TRANS_DP_CTL_A + (pipe) * 0x01000)
#define TRANS_DP_OUTPUT_ENABLE (1<<31)
#define TRANS_DP_PORT_SEL_B (0<<29)
#define TRANS_DP_PORT_SEL_C (1<<29)
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 31f08581e93a..989c19d2d959 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -256,7 +256,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
dev_priv->saveFPA1 = I915_READ(FPA1);
dev_priv->saveDPLL_A = I915_READ(DPLL_A);
}
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev))
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD);
dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A);
dev_priv->saveHBLANK_A = I915_READ(HBLANK_A);
@@ -294,7 +294,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
dev_priv->saveDSPASIZE = I915_READ(DSPASIZE);
dev_priv->saveDSPAPOS = I915_READ(DSPAPOS);
dev_priv->saveDSPAADDR = I915_READ(DSPAADDR);
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
dev_priv->saveDSPASURF = I915_READ(DSPASURF);
dev_priv->saveDSPATILEOFF = I915_READ(DSPATILEOFF);
}
@@ -313,7 +313,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
dev_priv->saveFPB1 = I915_READ(FPB1);
dev_priv->saveDPLL_B = I915_READ(DPLL_B);
}
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev))
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD);
dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B);
dev_priv->saveHBLANK_B = I915_READ(HBLANK_B);
@@ -351,7 +351,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
dev_priv->saveDSPBSIZE = I915_READ(DSPBSIZE);
dev_priv->saveDSPBPOS = I915_READ(DSPBPOS);
dev_priv->saveDSPBADDR = I915_READ(DSPBADDR);
- if (IS_I965GM(dev) || IS_GM45(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
dev_priv->saveDSPBSURF = I915_READ(DSPBSURF);
dev_priv->saveDSPBTILEOFF = I915_READ(DSPBTILEOFF);
}
@@ -404,7 +404,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A);
POSTING_READ(dpll_a_reg);
udelay(150);
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD);
POSTING_READ(DPLL_A_MD);
}
@@ -448,7 +448,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC);
I915_WRITE(DSPAADDR, dev_priv->saveDSPAADDR);
I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE);
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
I915_WRITE(DSPASURF, dev_priv->saveDSPASURF);
I915_WRITE(DSPATILEOFF, dev_priv->saveDSPATILEOFF);
}
@@ -473,7 +473,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B);
POSTING_READ(dpll_b_reg);
udelay(150);
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD);
POSTING_READ(DPLL_B_MD);
}
@@ -517,7 +517,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC);
I915_WRITE(DSPBADDR, dev_priv->saveDSPBADDR);
I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE);
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF);
I915_WRITE(DSPBTILEOFF, dev_priv->saveDSPBTILEOFF);
}
@@ -550,7 +550,7 @@ void i915_save_display(struct drm_device *dev)
dev_priv->saveCURBCNTR = I915_READ(CURBCNTR);
dev_priv->saveCURBPOS = I915_READ(CURBPOS);
dev_priv->saveCURBBASE = I915_READ(CURBBASE);
- if (!IS_I9XX(dev))
+ if (IS_GEN2(dev))
dev_priv->saveCURSIZE = I915_READ(CURSIZE);
/* CRT state */
@@ -573,7 +573,7 @@ void i915_save_display(struct drm_device *dev)
dev_priv->savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS);
dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
dev_priv->saveBLC_HIST_CTL = I915_READ(BLC_HIST_CTL);
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
if (IS_MOBILE(dev) && !IS_I830(dev))
dev_priv->saveLVDS = I915_READ(LVDS);
@@ -664,7 +664,7 @@ void i915_restore_display(struct drm_device *dev)
I915_WRITE(CURBPOS, dev_priv->saveCURBPOS);
I915_WRITE(CURBCNTR, dev_priv->saveCURBCNTR);
I915_WRITE(CURBBASE, dev_priv->saveCURBBASE);
- if (!IS_I9XX(dev))
+ if (IS_GEN2(dev))
I915_WRITE(CURSIZE, dev_priv->saveCURSIZE);
/* CRT state */
@@ -674,7 +674,7 @@ void i915_restore_display(struct drm_device *dev)
I915_WRITE(ADPA, dev_priv->saveADPA);
/* LVDS state */
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev))
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2);
if (HAS_PCH_SPLIT(dev)) {
@@ -878,9 +878,7 @@ int i915_restore_state(struct drm_device *dev)
for (i = 0; i < 3; i++)
I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);
- /* I2C state */
- intel_i2c_reset_gmbus(dev);
+ intel_i2c_reset(dev);
return 0;
}
-
diff --git a/drivers/gpu/drm/i915/intel_acpi.c b/drivers/gpu/drm/i915/intel_acpi.c
new file mode 100644
index 000000000000..65c88f9ba12c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_acpi.c
@@ -0,0 +1,286 @@
+/*
+ * Intel ACPI functions
+ *
+ * _DSM related code stolen from nouveau_acpi.c.
+ */
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/vga_switcheroo.h>
+#include <acpi/acpi_drivers.h>
+
+#include "drmP.h"
+
+#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
+
+#define INTEL_DSM_FN_SUPPORTED_FUNCTIONS 0 /* No args */
+#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
+
+static struct intel_dsm_priv {
+ acpi_handle dhandle;
+} intel_dsm_priv;
+
+static const u8 intel_dsm_guid[] = {
+ 0xd3, 0x73, 0xd8, 0x7e,
+ 0xd0, 0xc2,
+ 0x4f, 0x4e,
+ 0xa8, 0x54,
+ 0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
+};
+
+static int intel_dsm(acpi_handle handle, int func, int arg)
+{
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list input;
+ union acpi_object params[4];
+ union acpi_object *obj;
+ u32 result;
+ int ret = 0;
+
+ input.count = 4;
+ input.pointer = params;
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = sizeof(intel_dsm_guid);
+ params[0].buffer.pointer = (char *)intel_dsm_guid;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = INTEL_DSM_REVISION_ID;
+ params[2].type = ACPI_TYPE_INTEGER;
+ params[2].integer.value = func;
+ params[3].type = ACPI_TYPE_INTEGER;
+ params[3].integer.value = arg;
+
+ ret = acpi_evaluate_object(handle, "_DSM", &input, &output);
+ if (ret) {
+ DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
+ return ret;
+ }
+
+ obj = (union acpi_object *)output.pointer;
+
+ result = 0;
+ switch (obj->type) {
+ case ACPI_TYPE_INTEGER:
+ result = obj->integer.value;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+ if (obj->buffer.length == 4) {
+ result =(obj->buffer.pointer[0] |
+ (obj->buffer.pointer[1] << 8) |
+ (obj->buffer.pointer[2] << 16) |
+ (obj->buffer.pointer[3] << 24));
+ break;
+ }
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (result == 0x80000002)
+ ret = -ENODEV;
+
+ kfree(output.pointer);
+ return ret;
+}
+
+static char *intel_dsm_port_name(u8 id)
+{
+ switch (id) {
+ case 0:
+ return "Reserved";
+ case 1:
+ return "Analog VGA";
+ case 2:
+ return "LVDS";
+ case 3:
+ return "Reserved";
+ case 4:
+ return "HDMI/DVI_B";
+ case 5:
+ return "HDMI/DVI_C";
+ case 6:
+ return "HDMI/DVI_D";
+ case 7:
+ return "DisplayPort_A";
+ case 8:
+ return "DisplayPort_B";
+ case 9:
+ return "DisplayPort_C";
+ case 0xa:
+ return "DisplayPort_D";
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ return "Reserved";
+ case 0xe:
+ return "WiDi";
+ default:
+ return "bad type";
+ }
+}
+
+static char *intel_dsm_mux_type(u8 type)
+{
+ switch (type) {
+ case 0:
+ return "unknown";
+ case 1:
+ return "No MUX, iGPU only";
+ case 2:
+ return "No MUX, dGPU only";
+ case 3:
+ return "MUXed between iGPU and dGPU";
+ default:
+ return "bad type";
+ }
+}
+
+static void intel_dsm_platform_mux_info(void)
+{
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list input;
+ union acpi_object params[4];
+ union acpi_object *pkg;
+ int i, ret;
+
+ input.count = 4;
+ input.pointer = params;
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = sizeof(intel_dsm_guid);
+ params[0].buffer.pointer = (char *)intel_dsm_guid;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = INTEL_DSM_REVISION_ID;
+ params[2].type = ACPI_TYPE_INTEGER;
+ params[2].integer.value = INTEL_DSM_FN_PLATFORM_MUX_INFO;
+ params[3].type = ACPI_TYPE_INTEGER;
+ params[3].integer.value = 0;
+
+ ret = acpi_evaluate_object(intel_dsm_priv.dhandle, "_DSM", &input,
+ &output);
+ if (ret) {
+ DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
+ goto out;
+ }
+
+ pkg = (union acpi_object *)output.pointer;
+
+ if (pkg->type == ACPI_TYPE_PACKAGE) {
+ union acpi_object *connector_count = &pkg->package.elements[0];
+ DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
+ (unsigned long long)connector_count->integer.value);
+ for (i = 1; i < pkg->package.count; i++) {
+ union acpi_object *obj = &pkg->package.elements[i];
+ union acpi_object *connector_id =
+ &obj->package.elements[0];
+ union acpi_object *info = &obj->package.elements[1];
+ DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
+ (unsigned long long)connector_id->integer.value);
+ DRM_DEBUG_DRIVER(" port id: %s\n",
+ intel_dsm_port_name(info->buffer.pointer[0]));
+ DRM_DEBUG_DRIVER(" display mux info: %s\n",
+ intel_dsm_mux_type(info->buffer.pointer[1]));
+ DRM_DEBUG_DRIVER(" aux/dc mux info: %s\n",
+ intel_dsm_mux_type(info->buffer.pointer[2]));
+ DRM_DEBUG_DRIVER(" hpd mux info: %s\n",
+ intel_dsm_mux_type(info->buffer.pointer[3]));
+ }
+ } else {
+ DRM_ERROR("MUX INFO call failed\n");
+ }
+
+out:
+ kfree(output.pointer);
+}
+
+static int intel_dsm_switchto(enum vga_switcheroo_client_id id)
+{
+ return 0;
+}
+
+static int intel_dsm_power_state(enum vga_switcheroo_client_id id,
+ enum vga_switcheroo_state state)
+{
+ return 0;
+}
+
+static int intel_dsm_init(void)
+{
+ return 0;
+}
+
+static int intel_dsm_get_client_id(struct pci_dev *pdev)
+{
+ if (intel_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
+ return VGA_SWITCHEROO_IGD;
+ else
+ return VGA_SWITCHEROO_DIS;
+}
+
+static struct vga_switcheroo_handler intel_dsm_handler = {
+ .switchto = intel_dsm_switchto,
+ .power_state = intel_dsm_power_state,
+ .init = intel_dsm_init,
+ .get_client_id = intel_dsm_get_client_id,
+};
+
+static bool intel_dsm_pci_probe(struct pci_dev *pdev)
+{
+ acpi_handle dhandle, intel_handle;
+ acpi_status status;
+ int ret;
+
+ dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+ if (!dhandle)
+ return false;
+
+ status = acpi_get_handle(dhandle, "_DSM", &intel_handle);
+ if (ACPI_FAILURE(status)) {
+ DRM_DEBUG_KMS("no _DSM method for intel device\n");
+ return false;
+ }
+
+ ret = intel_dsm(dhandle, INTEL_DSM_FN_SUPPORTED_FUNCTIONS, 0);
+ if (ret < 0) {
+ DRM_ERROR("failed to get supported _DSM functions\n");
+ return false;
+ }
+
+ intel_dsm_priv.dhandle = dhandle;
+
+ intel_dsm_platform_mux_info();
+ return true;
+}
+
+static bool intel_dsm_detect(void)
+{
+ char acpi_method_name[255] = { 0 };
+ struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
+ struct pci_dev *pdev = NULL;
+ bool has_dsm = false;
+ int vga_count = 0;
+
+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
+ vga_count++;
+ has_dsm |= intel_dsm_pci_probe(pdev);
+ }
+
+ if (vga_count == 2 && has_dsm) {
+ acpi_get_name(intel_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
+ DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n",
+ acpi_method_name);
+ return true;
+ }
+
+ return false;
+}
+
+void intel_register_dsm_handler(void)
+{
+ if (!intel_dsm_detect())
+ return;
+
+ vga_switcheroo_register_handler(&intel_dsm_handler);
+}
+
+void intel_unregister_dsm_handler(void)
+{
+ vga_switcheroo_unregister_handler();
+}
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 96f75d7f6633..b0b1200ed650 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -24,6 +24,7 @@
* Eric Anholt <eric@anholt.net>
*
*/
+#include <drm/drm_dp_helper.h>
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
@@ -129,10 +130,6 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
int i, temp_downclock;
struct drm_display_mode *temp_mode;
- /* Defaults if we can't find VBT info */
- dev_priv->lvds_dither = 0;
- dev_priv->lvds_vbt = 0;
-
lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
if (!lvds_options)
return;
@@ -140,6 +137,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
dev_priv->lvds_dither = lvds_options->pixel_dither;
if (lvds_options->panel_type == 0xff)
return;
+
panel_type = lvds_options->panel_type;
lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
@@ -169,6 +167,8 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
((unsigned char *)entry + dvo_timing_offset);
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
+ if (!panel_fixed_mode)
+ return;
fill_detail_timing_data(panel_fixed_mode, dvo_timing);
@@ -230,8 +230,6 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv,
struct lvds_dvo_timing *dvo_timing;
struct drm_display_mode *panel_fixed_mode;
- dev_priv->sdvo_lvds_vbt_mode = NULL;
-
sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS);
if (!sdvo_lvds_options)
return;
@@ -260,10 +258,6 @@ parse_general_features(struct drm_i915_private *dev_priv,
struct drm_device *dev = dev_priv->dev;
struct bdb_general_features *general;
- /* Set sensible defaults in case we can't find the general block */
- dev_priv->int_tv_support = 1;
- dev_priv->int_crt_support = 1;
-
general = find_section(bdb, BDB_GENERAL_FEATURES);
if (general) {
dev_priv->int_tv_support = general->int_tv_support;
@@ -271,10 +265,10 @@ parse_general_features(struct drm_i915_private *dev_priv,
dev_priv->lvds_use_ssc = general->enable_ssc;
if (dev_priv->lvds_use_ssc) {
- if (IS_I85X(dev_priv->dev))
+ if (IS_I85X(dev))
dev_priv->lvds_ssc_freq =
general->ssc_freq ? 66 : 48;
- else if (IS_IRONLAKE(dev_priv->dev) || IS_GEN6(dev))
+ else if (IS_GEN5(dev) || IS_GEN6(dev))
dev_priv->lvds_ssc_freq =
general->ssc_freq ? 100 : 120;
else
@@ -289,14 +283,6 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
struct bdb_header *bdb)
{
struct bdb_general_definitions *general;
- const int crt_bus_map_table[] = {
- GPIOB,
- GPIOA,
- GPIOC,
- GPIOD,
- GPIOE,
- GPIOF,
- };
general = find_section(bdb, BDB_GENERAL_DEFINITIONS);
if (general) {
@@ -304,10 +290,8 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
if (block_size >= sizeof(*general)) {
int bus_pin = general->crt_ddc_gmbus_pin;
DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin);
- if ((bus_pin >= 1) && (bus_pin <= 6)) {
- dev_priv->crt_ddc_bus =
- crt_bus_map_table[bus_pin-1];
- }
+ if (bus_pin >= 1 && bus_pin <= 6)
+ dev_priv->crt_ddc_pin = bus_pin;
} else {
DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n",
block_size);
@@ -317,7 +301,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
static void
parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
- struct bdb_header *bdb)
+ struct bdb_header *bdb)
{
struct sdvo_device_mapping *p_mapping;
struct bdb_general_definitions *p_defs;
@@ -327,7 +311,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
if (!p_defs) {
- DRM_DEBUG_KMS("No general definition block is found\n");
+ DRM_DEBUG_KMS("No general definition block is found, unable to construct sdvo mapping.\n");
return;
}
/* judge whether the size of child device meets the requirements.
@@ -377,7 +361,16 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
p_mapping->slave_addr = p_child->slave_addr;
p_mapping->dvo_wiring = p_child->dvo_wiring;
p_mapping->ddc_pin = p_child->ddc_pin;
+ p_mapping->i2c_pin = p_child->i2c_pin;
+ p_mapping->i2c_speed = p_child->i2c_speed;
p_mapping->initialized = 1;
+ DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d, i2c_speed=%d\n",
+ p_mapping->dvo_port,
+ p_mapping->slave_addr,
+ p_mapping->dvo_wiring,
+ p_mapping->ddc_pin,
+ p_mapping->i2c_pin,
+ p_mapping->i2c_speed);
} else {
DRM_DEBUG_KMS("Maybe one SDVO port is shared by "
"two SDVO device.\n");
@@ -409,14 +402,11 @@ parse_driver_features(struct drm_i915_private *dev_priv,
if (!driver)
return;
- if (driver && SUPPORTS_EDP(dev) &&
- driver->lvds_config == BDB_DRIVER_FEATURE_EDP) {
- dev_priv->edp_support = 1;
- } else {
- dev_priv->edp_support = 0;
- }
+ if (SUPPORTS_EDP(dev) &&
+ driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
+ dev_priv->edp.support = 1;
- if (driver && driver->dual_frequency)
+ if (driver->dual_frequency)
dev_priv->render_reclock_avail = true;
}
@@ -424,27 +414,78 @@ static void
parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
{
struct bdb_edp *edp;
+ struct edp_power_seq *edp_pps;
+ struct edp_link_params *edp_link_params;
edp = find_section(bdb, BDB_EDP);
if (!edp) {
- if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp_support) {
+ if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp.support) {
DRM_DEBUG_KMS("No eDP BDB found but eDP panel "
- "supported, assume 18bpp panel color "
- "depth.\n");
- dev_priv->edp_bpp = 18;
+ "supported, assume %dbpp panel color "
+ "depth.\n",
+ dev_priv->edp.bpp);
}
return;
}
switch ((edp->color_depth >> (panel_type * 2)) & 3) {
case EDP_18BPP:
- dev_priv->edp_bpp = 18;
+ dev_priv->edp.bpp = 18;
break;
case EDP_24BPP:
- dev_priv->edp_bpp = 24;
+ dev_priv->edp.bpp = 24;
break;
case EDP_30BPP:
- dev_priv->edp_bpp = 30;
+ dev_priv->edp.bpp = 30;
+ break;
+ }
+
+ /* Get the eDP sequencing and link info */
+ edp_pps = &edp->power_seqs[panel_type];
+ edp_link_params = &edp->link_params[panel_type];
+
+ dev_priv->edp.pps = *edp_pps;
+
+ dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 :
+ DP_LINK_BW_1_62;
+ switch (edp_link_params->lanes) {
+ case 0:
+ dev_priv->edp.lanes = 1;
+ break;
+ case 1:
+ dev_priv->edp.lanes = 2;
+ break;
+ case 3:
+ default:
+ dev_priv->edp.lanes = 4;
+ break;
+ }
+ switch (edp_link_params->preemphasis) {
+ case 0:
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
+ break;
+ case 1:
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
+ break;
+ case 2:
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
+ break;
+ case 3:
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
+ break;
+ }
+ switch (edp_link_params->vswing) {
+ case 0:
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400;
+ break;
+ case 1:
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600;
+ break;
+ case 2:
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800;
+ break;
+ case 3:
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200;
break;
}
}
@@ -460,7 +501,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
if (!p_defs) {
- DRM_DEBUG_KMS("No general definition block is found\n");
+ DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n");
return;
}
/* judge whether the size of child device meets the requirements.
@@ -513,50 +554,83 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
}
return;
}
+
+static void
+init_vbt_defaults(struct drm_i915_private *dev_priv)
+{
+ dev_priv->crt_ddc_pin = GMBUS_PORT_VGADDC;
+
+ /* LFP panel data */
+ dev_priv->lvds_dither = 1;
+ dev_priv->lvds_vbt = 0;
+
+ /* SDVO panel data */
+ dev_priv->sdvo_lvds_vbt_mode = NULL;
+
+ /* general features */
+ dev_priv->int_tv_support = 1;
+ dev_priv->int_crt_support = 1;
+ dev_priv->lvds_use_ssc = 0;
+
+ /* eDP data */
+ dev_priv->edp.bpp = 18;
+}
+
/**
- * intel_init_bios - initialize VBIOS settings & find VBT
+ * intel_parse_bios - find VBT and initialize settings from the BIOS
* @dev: DRM device
*
* Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
* to appropriate values.
*
- * VBT existence is a sanity check that is relied on by other i830_bios.c code.
- * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
- * feed an updated VBT back through that, compared to what we'll fetch using
- * this method of groping around in the BIOS data.
- *
* Returns 0 on success, nonzero on failure.
*/
bool
-intel_init_bios(struct drm_device *dev)
+intel_parse_bios(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct pci_dev *pdev = dev->pdev;
- struct vbt_header *vbt = NULL;
- struct bdb_header *bdb;
- u8 __iomem *bios;
- size_t size;
- int i;
-
- bios = pci_map_rom(pdev, &size);
- if (!bios)
- return -1;
-
- /* Scour memory looking for the VBT signature */
- for (i = 0; i + 4 < size; i++) {
- if (!memcmp(bios + i, "$VBT", 4)) {
- vbt = (struct vbt_header *)(bios + i);
- break;
- }
+ struct bdb_header *bdb = NULL;
+ u8 __iomem *bios = NULL;
+
+ init_vbt_defaults(dev_priv);
+
+ /* XXX Should this validation be moved to intel_opregion.c? */
+ if (dev_priv->opregion.vbt) {
+ struct vbt_header *vbt = dev_priv->opregion.vbt;
+ if (memcmp(vbt->signature, "$VBT", 4) == 0) {
+ DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n",
+ vbt->signature);
+ bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
+ } else
+ dev_priv->opregion.vbt = NULL;
}
- if (!vbt) {
- DRM_ERROR("VBT signature missing\n");
- pci_unmap_rom(pdev, bios);
- return -1;
- }
+ if (bdb == NULL) {
+ struct vbt_header *vbt = NULL;
+ size_t size;
+ int i;
- bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
+ bios = pci_map_rom(pdev, &size);
+ if (!bios)
+ return -1;
+
+ /* Scour memory looking for the VBT signature */
+ for (i = 0; i + 4 < size; i++) {
+ if (!memcmp(bios + i, "$VBT", 4)) {
+ vbt = (struct vbt_header *)(bios + i);
+ break;
+ }
+ }
+
+ if (!vbt) {
+ DRM_ERROR("VBT signature missing\n");
+ pci_unmap_rom(pdev, bios);
+ return -1;
+ }
+
+ bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
+ }
/* Grab useful general definitions */
parse_general_features(dev_priv, bdb);
@@ -568,7 +642,25 @@ intel_init_bios(struct drm_device *dev)
parse_driver_features(dev_priv, bdb);
parse_edp(dev_priv, bdb);
- pci_unmap_rom(pdev, bios);
+ if (bios)
+ pci_unmap_rom(pdev, bios);
return 0;
}
+
+/* Ensure that vital registers have been initialised, even if the BIOS
+ * is absent or just failing to do its job.
+ */
+void intel_setup_bios(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* Set the Panel Power On/Off timings if uninitialized. */
+ if ((I915_READ(PP_ON_DELAYS) == 0) && (I915_READ(PP_OFF_DELAYS) == 0)) {
+ /* Set T2 to 40ms and T5 to 200ms */
+ I915_WRITE(PP_ON_DELAYS, 0x019007d0);
+
+ /* Set T3 to 35ms and Tx to 200ms */
+ I915_WRITE(PP_OFF_DELAYS, 0x015e07d0);
+ }
+}
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index 4c18514f6f80..5f8e4edcbbb9 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -197,7 +197,8 @@ struct bdb_general_features {
struct child_device_config {
u16 handle;
u16 device_type;
- u8 device_id[10]; /* See DEVICE_TYPE_* above */
+ u8 i2c_speed;
+ u8 rsvd[9];
u16 addin_offset;
u8 dvo_port; /* See Device_PORT_* above */
u8 i2c_pin;
@@ -466,7 +467,8 @@ struct bdb_edp {
struct edp_link_params link_params[16];
} __attribute__ ((packed));
-bool intel_init_bios(struct drm_device *dev);
+void intel_setup_bios(struct drm_device *dev);
+bool intel_parse_bios(struct drm_device *dev);
/*
* Driver<->VBIOS interaction occurs through scratch bits in
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 197d4f32585a..c55c77043357 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -79,7 +79,7 @@ static int intel_crt_mode_valid(struct drm_connector *connector,
if (mode->clock < 25000)
return MODE_CLOCK_LOW;
- if (!IS_I9XX(dev))
+ if (IS_GEN2(dev))
max_clock = 350000;
else
max_clock = 400000;
@@ -123,7 +123,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
* Disable separate mode multiplier used when cloning SDVO to CRT
* XXX this needs to be adjusted when we really are cloning
*/
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
dpll_md = I915_READ(dpll_md_reg);
I915_WRITE(dpll_md_reg,
dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
@@ -187,11 +187,12 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
I915_WRITE(PCH_ADPA, adpa);
if (wait_for((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
- 1000, 1))
+ 1000))
DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER");
if (turn_off_dac) {
- I915_WRITE(PCH_ADPA, temp);
+ /* Make sure hotplug is enabled */
+ I915_WRITE(PCH_ADPA, temp | ADPA_CRT_HOTPLUG_ENABLE);
(void)I915_READ(PCH_ADPA);
}
@@ -244,7 +245,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
/* wait for FORCE_DETECT to go off */
if (wait_for((I915_READ(PORT_HOTPLUG_EN) &
CRT_HOTPLUG_FORCE_DETECT) == 0,
- 1000, 1))
+ 1000))
DRM_DEBUG_KMS("timed out waiting for FORCE_DETECT to go off");
}
@@ -261,21 +262,47 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
return ret;
}
+static bool intel_crt_ddc_probe(struct drm_i915_private *dev_priv, int ddc_bus)
+{
+ u8 buf;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = 0xA0,
+ .flags = 0,
+ .len = 1,
+ .buf = &buf,
+ },
+ };
+ /* DDC monitor detect: Does it ACK a write to 0xA0? */
+ return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 1) == 1;
+}
+
static bool intel_crt_detect_ddc(struct drm_encoder *encoder)
{
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+ struct drm_i915_private *dev_priv = encoder->dev->dev_private;
/* CRT should always be at 0, but check anyway */
if (intel_encoder->type != INTEL_OUTPUT_ANALOG)
return false;
- return intel_ddc_probe(intel_encoder);
+ if (intel_crt_ddc_probe(dev_priv, dev_priv->crt_ddc_pin)) {
+ DRM_DEBUG_KMS("CRT detected via DDC:0xa0\n");
+ return true;
+ }
+
+ if (intel_ddc_probe(intel_encoder, dev_priv->crt_ddc_pin)) {
+ DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
+ return true;
+ }
+
+ return false;
}
static enum drm_connector_status
intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder)
{
- struct drm_encoder *encoder = &intel_encoder->enc;
+ struct drm_encoder *encoder = &intel_encoder->base;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -295,6 +322,8 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder
uint8_t st00;
enum drm_connector_status status;
+ DRM_DEBUG_KMS("starting load-detect on CRT\n");
+
if (pipe == 0) {
bclrpat_reg = BCLRPAT_A;
vtotal_reg = VTOTAL_A;
@@ -324,9 +353,10 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder
/* Set the border color to purple. */
I915_WRITE(bclrpat_reg, 0x500050);
- if (IS_I9XX(dev)) {
+ if (!IS_GEN2(dev)) {
uint32_t pipeconf = I915_READ(pipeconf_reg);
I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER);
+ POSTING_READ(pipeconf_reg);
/* Wait for next Vblank to substitue
* border color for Color info */
intel_wait_for_vblank(dev, pipe);
@@ -404,34 +434,37 @@ static enum drm_connector_status
intel_crt_detect(struct drm_connector *connector, bool force)
{
struct drm_device *dev = connector->dev;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
+ struct intel_encoder *encoder = intel_attached_encoder(connector);
struct drm_crtc *crtc;
int dpms_mode;
enum drm_connector_status status;
- if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
- if (intel_crt_detect_hotplug(connector))
+ if (I915_HAS_HOTPLUG(dev)) {
+ if (intel_crt_detect_hotplug(connector)) {
+ DRM_DEBUG_KMS("CRT detected via hotplug\n");
return connector_status_connected;
- else
+ } else
return connector_status_disconnected;
}
- if (intel_crt_detect_ddc(encoder))
+ if (intel_crt_detect_ddc(&encoder->base))
return connector_status_connected;
if (!force)
return connector->status;
/* for pre-945g platforms use load detect */
- if (encoder->crtc && encoder->crtc->enabled) {
- status = intel_crt_load_detect(encoder->crtc, intel_encoder);
+ if (encoder->base.crtc && encoder->base.crtc->enabled) {
+ status = intel_crt_load_detect(encoder->base.crtc, encoder);
} else {
- crtc = intel_get_load_detect_pipe(intel_encoder, connector,
+ crtc = intel_get_load_detect_pipe(encoder, connector,
NULL, &dpms_mode);
if (crtc) {
- status = intel_crt_load_detect(crtc, intel_encoder);
- intel_release_load_detect_pipe(intel_encoder,
+ if (intel_crt_detect_ddc(&encoder->base))
+ status = connector_status_connected;
+ else
+ status = intel_crt_load_detect(crtc, encoder);
+ intel_release_load_detect_pipe(encoder,
connector, dpms_mode);
} else
status = connector_status_unknown;
@@ -449,32 +482,18 @@ static void intel_crt_destroy(struct drm_connector *connector)
static int intel_crt_get_modes(struct drm_connector *connector)
{
- int ret;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
- struct i2c_adapter *ddc_bus;
struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
-
- ret = intel_ddc_get_modes(connector, intel_encoder->ddc_bus);
+ ret = intel_ddc_get_modes(connector,
+ &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
if (ret || !IS_G4X(dev))
- goto end;
+ return ret;
/* Try to probe digital port for output in DVI-I -> VGA mode. */
- ddc_bus = intel_i2c_create(connector->dev, GPIOD, "CRTDDC_D");
-
- if (!ddc_bus) {
- dev_printk(KERN_ERR, &connector->dev->pdev->dev,
- "DDC bus registration failed for CRTDDC_D.\n");
- goto end;
- }
- /* Try to get modes by GPIOD port */
- ret = intel_ddc_get_modes(connector, ddc_bus);
- intel_i2c_destroy(ddc_bus);
-
-end:
- return ret;
-
+ return intel_ddc_get_modes(connector,
+ &dev_priv->gmbus[GMBUS_PORT_DPB].adapter);
}
static int intel_crt_set_property(struct drm_connector *connector,
@@ -507,7 +526,7 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = {
static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
.mode_valid = intel_crt_mode_valid,
.get_modes = intel_crt_get_modes,
- .best_encoder = intel_attached_encoder,
+ .best_encoder = intel_best_encoder,
};
static const struct drm_encoder_funcs intel_crt_enc_funcs = {
@@ -520,7 +539,6 @@ void intel_crt_init(struct drm_device *dev)
struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 i2c_reg;
intel_encoder = kzalloc(sizeof(struct intel_encoder), GFP_KERNEL);
if (!intel_encoder)
@@ -536,27 +554,10 @@ void intel_crt_init(struct drm_device *dev)
drm_connector_init(dev, &intel_connector->base,
&intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
- drm_encoder_init(dev, &intel_encoder->enc, &intel_crt_enc_funcs,
+ drm_encoder_init(dev, &intel_encoder->base, &intel_crt_enc_funcs,
DRM_MODE_ENCODER_DAC);
- drm_mode_connector_attach_encoder(&intel_connector->base,
- &intel_encoder->enc);
-
- /* Set up the DDC bus. */
- if (HAS_PCH_SPLIT(dev))
- i2c_reg = PCH_GPIOA;
- else {
- i2c_reg = GPIOA;
- /* Use VBT information for CRT DDC if available */
- if (dev_priv->crt_ddc_bus != 0)
- i2c_reg = dev_priv->crt_ddc_bus;
- }
- intel_encoder->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
- if (!intel_encoder->ddc_bus) {
- dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
- "failed.\n");
- return;
- }
+ intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_ANALOG;
intel_encoder->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
@@ -566,7 +567,7 @@ void intel_crt_init(struct drm_device *dev)
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
- drm_encoder_helper_add(&intel_encoder->enc, &intel_crt_helper_funcs);
+ drm_encoder_helper_add(&intel_encoder->base, &intel_crt_helper_funcs);
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
drm_sysfs_connector_add(connector);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 979228594599..990f065374b2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -43,8 +43,8 @@
bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
static void intel_update_watermarks(struct drm_device *dev);
-static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule);
-static void intel_crtc_update_cursor(struct drm_crtc *crtc);
+static void intel_increase_pllclock(struct drm_crtc *crtc);
+static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
typedef struct {
/* given values */
@@ -342,6 +342,16 @@ static bool
intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock);
+static inline u32 /* units of 100MHz */
+intel_fdi_link_freq(struct drm_device *dev)
+{
+ if (IS_GEN5(dev)) {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ return (I915_READ(FDI_PLL_BIOS_0) & FDI_PLL_FB_CLOCK_MASK) + 2;
+ } else
+ return 27;
+}
+
static const intel_limit_t intel_limits_i8xx_dvo = {
.dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
.vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
@@ -701,16 +711,16 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
limit = intel_ironlake_limit(crtc);
else if (IS_G4X(dev)) {
limit = intel_g4x_limit(crtc);
- } else if (IS_I9XX(dev) && !IS_PINEVIEW(dev)) {
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
- limit = &intel_limits_i9xx_lvds;
- else
- limit = &intel_limits_i9xx_sdvo;
} else if (IS_PINEVIEW(dev)) {
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
limit = &intel_limits_pineview_lvds;
else
limit = &intel_limits_pineview_sdvo;
+ } else if (!IS_GEN2(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits_i9xx_lvds;
+ else
+ limit = &intel_limits_i9xx_sdvo;
} else {
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
limit = &intel_limits_i8xx_lvds;
@@ -744,20 +754,17 @@ static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock
/**
* Returns whether any output on the specified pipe is of the specified type
*/
-bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
+bool 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;
- struct drm_encoder *l_entry;
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct intel_encoder *encoder;
+
+ list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+ if (encoder->base.crtc == crtc && encoder->type == type)
+ return true;
- list_for_each_entry(l_entry, &mode_config->encoder_list, head) {
- if (l_entry && l_entry->crtc == crtc) {
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(l_entry);
- if (intel_encoder->type == type)
- return true;
- }
- }
- return false;
+ return false;
}
#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0)
@@ -928,10 +935,6 @@ intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
intel_clock_t clock;
- /* return directly when it is eDP */
- if (HAS_eDP)
- return true;
-
if (target < 200000) {
clock.n = 1;
clock.p1 = 2;
@@ -955,26 +958,26 @@ 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)
{
- intel_clock_t clock;
- if (target < 200000) {
- clock.p1 = 2;
- clock.p2 = 10;
- clock.n = 2;
- clock.m1 = 23;
- clock.m2 = 8;
- } else {
- clock.p1 = 1;
- clock.p2 = 10;
- clock.n = 1;
- clock.m1 = 14;
- clock.m2 = 2;
- }
- clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2);
- clock.p = (clock.p1 * clock.p2);
- clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p;
- clock.vco = 0;
- memcpy(best_clock, &clock, sizeof(intel_clock_t));
- return true;
+ intel_clock_t clock;
+ if (target < 200000) {
+ clock.p1 = 2;
+ clock.p2 = 10;
+ clock.n = 2;
+ clock.m1 = 23;
+ clock.m2 = 8;
+ } else {
+ clock.p1 = 1;
+ clock.p2 = 10;
+ clock.n = 1;
+ clock.m1 = 14;
+ clock.m2 = 2;
+ }
+ clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2);
+ clock.p = (clock.p1 * clock.p2);
+ clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p;
+ clock.vco = 0;
+ memcpy(best_clock, &clock, sizeof(intel_clock_t));
+ return true;
}
/**
@@ -1007,9 +1010,9 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe)
I915_READ(pipestat_reg) | PIPE_VBLANK_INTERRUPT_STATUS);
/* Wait for vblank interrupt bit to set */
- if (wait_for((I915_READ(pipestat_reg) &
- PIPE_VBLANK_INTERRUPT_STATUS),
- 50, 0))
+ if (wait_for(I915_READ(pipestat_reg) &
+ PIPE_VBLANK_INTERRUPT_STATUS,
+ 50))
DRM_DEBUG_KMS("vblank wait timed out\n");
}
@@ -1028,36 +1031,35 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe)
* Otherwise:
* wait for the display line value to settle (it usually
* ends up stopping at the start of the next frame).
- *
+ *
*/
-static void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
+void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (INTEL_INFO(dev)->gen >= 4) {
- int pipeconf_reg = (pipe == 0 ? PIPEACONF : PIPEBCONF);
+ int reg = PIPECONF(pipe);
/* Wait for the Pipe State to go off */
- if (wait_for((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) == 0,
- 100, 0))
+ if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0,
+ 100))
DRM_DEBUG_KMS("pipe_off wait timed out\n");
} else {
u32 last_line;
- int pipedsl_reg = (pipe == 0 ? PIPEADSL : PIPEBDSL);
+ int reg = PIPEDSL(pipe);
unsigned long timeout = jiffies + msecs_to_jiffies(100);
/* Wait for the display line to settle */
do {
- last_line = I915_READ(pipedsl_reg) & DSL_LINEMASK;
+ last_line = I915_READ(reg) & DSL_LINEMASK;
mdelay(5);
- } while (((I915_READ(pipedsl_reg) & DSL_LINEMASK) != last_line) &&
+ } while (((I915_READ(reg) & DSL_LINEMASK) != last_line) &&
time_after(timeout, jiffies));
if (time_after(jiffies, timeout))
DRM_DEBUG_KMS("pipe_off wait timed out\n");
}
}
-/* Parameters have changed, update FBC info */
static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
{
struct drm_device *dev = crtc->dev;
@@ -1069,6 +1071,14 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
int plane, i;
u32 fbc_ctl, fbc_ctl2;
+ if (fb->pitch == dev_priv->cfb_pitch &&
+ obj_priv->fence_reg == dev_priv->cfb_fence &&
+ intel_crtc->plane == dev_priv->cfb_plane &&
+ I915_READ(FBC_CONTROL) & FBC_CTL_EN)
+ return;
+
+ i8xx_disable_fbc(dev);
+
dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
if (fb->pitch < dev_priv->cfb_pitch)
@@ -1102,7 +1112,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
I915_WRITE(FBC_CONTROL, fbc_ctl);
DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
- dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
+ dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
}
void i8xx_disable_fbc(struct drm_device *dev)
@@ -1110,19 +1120,16 @@ void i8xx_disable_fbc(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 fbc_ctl;
- if (!I915_HAS_FBC(dev))
- return;
-
- if (!(I915_READ(FBC_CONTROL) & FBC_CTL_EN))
- return; /* Already off, just return */
-
/* Disable compression */
fbc_ctl = I915_READ(FBC_CONTROL);
+ if ((fbc_ctl & FBC_CTL_EN) == 0)
+ return;
+
fbc_ctl &= ~FBC_CTL_EN;
I915_WRITE(FBC_CONTROL, fbc_ctl);
/* Wait for compressing bit to clear */
- if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10, 0)) {
+ if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) {
DRM_DEBUG_KMS("FBC idle timed out\n");
return;
}
@@ -1145,14 +1152,27 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int plane = (intel_crtc->plane == 0 ? DPFC_CTL_PLANEA :
- DPFC_CTL_PLANEB);
+ int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
unsigned long stall_watermark = 200;
u32 dpfc_ctl;
+ dpfc_ctl = I915_READ(DPFC_CONTROL);
+ if (dpfc_ctl & DPFC_CTL_EN) {
+ if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
+ dev_priv->cfb_fence == obj_priv->fence_reg &&
+ dev_priv->cfb_plane == intel_crtc->plane &&
+ dev_priv->cfb_y == crtc->y)
+ return;
+
+ I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
+ POSTING_READ(DPFC_CONTROL);
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+ }
+
dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
dev_priv->cfb_fence = obj_priv->fence_reg;
dev_priv->cfb_plane = intel_crtc->plane;
+ dev_priv->cfb_y = crtc->y;
dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
if (obj_priv->tiling_mode != I915_TILING_NONE) {
@@ -1162,7 +1182,6 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY);
}
- I915_WRITE(DPFC_CONTROL, dpfc_ctl);
I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
(interval << DPFC_RECOMP_TIMER_COUNT_SHIFT));
@@ -1181,10 +1200,12 @@ void g4x_disable_fbc(struct drm_device *dev)
/* Disable compression */
dpfc_ctl = I915_READ(DPFC_CONTROL);
- dpfc_ctl &= ~DPFC_CTL_EN;
- I915_WRITE(DPFC_CONTROL, dpfc_ctl);
+ if (dpfc_ctl & DPFC_CTL_EN) {
+ dpfc_ctl &= ~DPFC_CTL_EN;
+ I915_WRITE(DPFC_CONTROL, dpfc_ctl);
- DRM_DEBUG_KMS("disabled FBC\n");
+ DRM_DEBUG_KMS("disabled FBC\n");
+ }
}
static bool g4x_fbc_enabled(struct drm_device *dev)
@@ -1202,16 +1223,30 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int plane = (intel_crtc->plane == 0) ? DPFC_CTL_PLANEA :
- DPFC_CTL_PLANEB;
+ int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
unsigned long stall_watermark = 200;
u32 dpfc_ctl;
+ dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
+ if (dpfc_ctl & DPFC_CTL_EN) {
+ if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
+ dev_priv->cfb_fence == obj_priv->fence_reg &&
+ dev_priv->cfb_plane == intel_crtc->plane &&
+ dev_priv->cfb_offset == obj_priv->gtt_offset &&
+ dev_priv->cfb_y == crtc->y)
+ return;
+
+ I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
+ POSTING_READ(ILK_DPFC_CONTROL);
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+ }
+
dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
dev_priv->cfb_fence = obj_priv->fence_reg;
dev_priv->cfb_plane = intel_crtc->plane;
+ dev_priv->cfb_offset = obj_priv->gtt_offset;
+ dev_priv->cfb_y = crtc->y;
- dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
dpfc_ctl &= DPFC_RESERVED;
dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
if (obj_priv->tiling_mode != I915_TILING_NONE) {
@@ -1221,15 +1256,13 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY);
}
- I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
(stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
(interval << DPFC_RECOMP_TIMER_COUNT_SHIFT));
I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y);
I915_WRITE(ILK_FBC_RT_BASE, obj_priv->gtt_offset | ILK_FBC_RT_VALID);
/* enable it... */
- I915_WRITE(ILK_DPFC_CONTROL, I915_READ(ILK_DPFC_CONTROL) |
- DPFC_CTL_EN);
+ I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
}
@@ -1241,10 +1274,12 @@ void ironlake_disable_fbc(struct drm_device *dev)
/* Disable compression */
dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
- dpfc_ctl &= ~DPFC_CTL_EN;
- I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
+ if (dpfc_ctl & DPFC_CTL_EN) {
+ dpfc_ctl &= ~DPFC_CTL_EN;
+ I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl);
- DRM_DEBUG_KMS("disabled FBC\n");
+ DRM_DEBUG_KMS("disabled FBC\n");
+ }
}
static bool ironlake_fbc_enabled(struct drm_device *dev)
@@ -1286,8 +1321,7 @@ void intel_disable_fbc(struct drm_device *dev)
/**
* intel_update_fbc - enable/disable FBC as needed
- * @crtc: CRTC to point the compressor at
- * @mode: mode in use
+ * @dev: the drm_device
*
* Set up the framebuffer compression hardware at mode set time. We
* enable it if possible:
@@ -1304,18 +1338,14 @@ void intel_disable_fbc(struct drm_device *dev)
*
* We need to enable/disable FBC on a global basis.
*/
-static void intel_update_fbc(struct drm_crtc *crtc,
- struct drm_display_mode *mode)
+static void intel_update_fbc(struct drm_device *dev)
{
- struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_crtc *crtc = NULL, *tmp_crtc;
+ struct intel_crtc *intel_crtc;
+ struct drm_framebuffer *fb;
struct intel_framebuffer *intel_fb;
struct drm_i915_gem_object *obj_priv;
- struct drm_crtc *tmp_crtc;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int plane = intel_crtc->plane;
- int crtcs_enabled = 0;
DRM_DEBUG_KMS("\n");
@@ -1325,12 +1355,6 @@ static void intel_update_fbc(struct drm_crtc *crtc,
if (!I915_HAS_FBC(dev))
return;
- if (!crtc->fb)
- return;
-
- intel_fb = to_intel_framebuffer(fb);
- obj_priv = to_intel_bo(intel_fb->obj);
-
/*
* If FBC is already on, we just have to verify that we can
* keep it that way...
@@ -1341,35 +1365,47 @@ static void intel_update_fbc(struct drm_crtc *crtc,
* - going to an unsupported config (interlace, pixel multiply, etc.)
*/
list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) {
- if (tmp_crtc->enabled)
- crtcs_enabled++;
+ if (tmp_crtc->enabled) {
+ if (crtc) {
+ DRM_DEBUG_KMS("more than one pipe active, disabling compression\n");
+ dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES;
+ goto out_disable;
+ }
+ crtc = tmp_crtc;
+ }
}
- DRM_DEBUG_KMS("%d pipes active\n", crtcs_enabled);
- if (crtcs_enabled > 1) {
- DRM_DEBUG_KMS("more than one pipe active, disabling compression\n");
- dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES;
+
+ if (!crtc || crtc->fb == NULL) {
+ DRM_DEBUG_KMS("no output, disabling\n");
+ dev_priv->no_fbc_reason = FBC_NO_OUTPUT;
goto out_disable;
}
+
+ intel_crtc = to_intel_crtc(crtc);
+ fb = crtc->fb;
+ intel_fb = to_intel_framebuffer(fb);
+ obj_priv = to_intel_bo(intel_fb->obj);
+
if (intel_fb->obj->size > dev_priv->cfb_size) {
DRM_DEBUG_KMS("framebuffer too large, disabling "
- "compression\n");
+ "compression\n");
dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
goto out_disable;
}
- if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
- (mode->flags & DRM_MODE_FLAG_DBLSCAN)) {
+ if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) ||
+ (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) {
DRM_DEBUG_KMS("mode incompatible with compression, "
- "disabling\n");
+ "disabling\n");
dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE;
goto out_disable;
}
- if ((mode->hdisplay > 2048) ||
- (mode->vdisplay > 1536)) {
+ if ((crtc->mode.hdisplay > 2048) ||
+ (crtc->mode.vdisplay > 1536)) {
DRM_DEBUG_KMS("mode too large for compression, disabling\n");
dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE;
goto out_disable;
}
- if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) {
+ if ((IS_I915GM(dev) || IS_I945GM(dev)) && intel_crtc->plane != 0) {
DRM_DEBUG_KMS("plane not 0, disabling compression\n");
dev_priv->no_fbc_reason = FBC_BAD_PLANE;
goto out_disable;
@@ -1384,18 +1420,7 @@ static void intel_update_fbc(struct drm_crtc *crtc,
if (in_dbg_master())
goto out_disable;
- if (intel_fbc_enabled(dev)) {
- /* We can re-enable it in this case, but need to update pitch */
- if ((fb->pitch > dev_priv->cfb_pitch) ||
- (obj_priv->fence_reg != dev_priv->cfb_fence) ||
- (plane != dev_priv->cfb_plane))
- intel_disable_fbc(dev);
- }
-
- /* Now try to turn it back on if possible */
- if (!intel_fbc_enabled(dev))
- intel_enable_fbc(crtc, 500);
-
+ intel_enable_fbc(crtc, 500);
return;
out_disable:
@@ -1407,7 +1432,9 @@ out_disable:
}
int
-intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
+intel_pin_and_fence_fb_obj(struct drm_device *dev,
+ struct drm_gem_object *obj,
+ bool pipelined)
{
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
u32 alignment;
@@ -1417,7 +1444,7 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
case I915_TILING_NONE:
if (IS_BROADWATER(dev) || IS_CRESTLINE(dev))
alignment = 128 * 1024;
- else if (IS_I965G(dev))
+ else if (INTEL_INFO(dev)->gen >= 4)
alignment = 4 * 1024;
else
alignment = 64 * 1024;
@@ -1435,9 +1462,13 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
}
ret = i915_gem_object_pin(obj, alignment);
- if (ret != 0)
+ if (ret)
return ret;
+ ret = i915_gem_object_set_to_display_plane(obj, pipelined);
+ if (ret)
+ goto err_unpin;
+
/* Install a fence for tiled scan-out. Pre-i965 always needs a
* fence, whereas 965+ only requires a fence if using
* framebuffer compression. For simplicity, we always install
@@ -1445,20 +1476,22 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
*/
if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
obj_priv->tiling_mode != I915_TILING_NONE) {
- ret = i915_gem_object_get_fence_reg(obj);
- if (ret != 0) {
- i915_gem_object_unpin(obj);
- return ret;
- }
+ ret = i915_gem_object_get_fence_reg(obj, false);
+ if (ret)
+ goto err_unpin;
}
return 0;
+
+err_unpin:
+ i915_gem_object_unpin(obj);
+ return ret;
}
/* Assume fb object is pinned & idle & fenced and just update base pointers */
static int
intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
- int x, int y)
+ int x, int y, enum mode_set_atomic state)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1468,12 +1501,8 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct drm_gem_object *obj;
int plane = intel_crtc->plane;
unsigned long Start, Offset;
- int dspbase = (plane == 0 ? DSPAADDR : DSPBADDR);
- int dspsurf = (plane == 0 ? DSPASURF : DSPBSURF);
- int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE;
- int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF);
- int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
u32 dspcntr;
+ u32 reg;
switch (plane) {
case 0:
@@ -1488,7 +1517,8 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
obj = intel_fb->obj;
obj_priv = to_intel_bo(obj);
- dspcntr = I915_READ(dspcntr_reg);
+ reg = DSPCNTR(plane);
+ dspcntr = I915_READ(reg);
/* Mask out pixel format bits in case we change it */
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (fb->bits_per_pixel) {
@@ -1509,7 +1539,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
DRM_ERROR("Unknown color depth\n");
return -EINVAL;
}
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
if (obj_priv->tiling_mode != I915_TILING_NONE)
dspcntr |= DISPPLANE_TILED;
else
@@ -1520,28 +1550,24 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
/* must disable */
dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
- I915_WRITE(dspcntr_reg, dspcntr);
+ I915_WRITE(reg, dspcntr);
Start = obj_priv->gtt_offset;
Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8);
DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
Start, Offset, x, y, fb->pitch);
- I915_WRITE(dspstride, fb->pitch);
- if (IS_I965G(dev)) {
- I915_WRITE(dspsurf, Start);
- I915_WRITE(dsptileoff, (y << 16) | x);
- I915_WRITE(dspbase, Offset);
- } else {
- I915_WRITE(dspbase, Start + Offset);
- }
- POSTING_READ(dspbase);
-
- if (IS_I965G(dev) || plane == 0)
- intel_update_fbc(crtc, &crtc->mode);
+ I915_WRITE(DSPSTRIDE(plane), fb->pitch);
+ if (INTEL_INFO(dev)->gen >= 4) {
+ I915_WRITE(DSPSURF(plane), Start);
+ I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
+ I915_WRITE(DSPADDR(plane), Offset);
+ } else
+ I915_WRITE(DSPADDR(plane), Start + Offset);
+ POSTING_READ(reg);
- intel_wait_for_vblank(dev, intel_crtc->pipe);
- intel_increase_pllclock(crtc, true);
+ intel_update_fbc(dev);
+ intel_increase_pllclock(crtc);
return 0;
}
@@ -1553,11 +1579,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_device *dev = crtc->dev;
struct drm_i915_master_private *master_priv;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct intel_framebuffer *intel_fb;
- struct drm_i915_gem_object *obj_priv;
- struct drm_gem_object *obj;
- int pipe = intel_crtc->pipe;
- int plane = intel_crtc->plane;
int ret;
/* no fb bound */
@@ -1566,45 +1587,42 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
}
- switch (plane) {
+ switch (intel_crtc->plane) {
case 0:
case 1:
break;
default:
- DRM_ERROR("Can't update plane %d in SAREA\n", plane);
return -EINVAL;
}
- intel_fb = to_intel_framebuffer(crtc->fb);
- obj = intel_fb->obj;
- obj_priv = to_intel_bo(obj);
-
mutex_lock(&dev->struct_mutex);
- ret = intel_pin_and_fence_fb_obj(dev, obj);
+ ret = intel_pin_and_fence_fb_obj(dev,
+ to_intel_framebuffer(crtc->fb)->obj,
+ false);
if (ret != 0) {
mutex_unlock(&dev->struct_mutex);
return ret;
}
- ret = i915_gem_object_set_to_display_plane(obj);
- if (ret != 0) {
- i915_gem_object_unpin(obj);
- mutex_unlock(&dev->struct_mutex);
- return ret;
+ if (old_fb) {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_gem_object *obj = to_intel_framebuffer(old_fb)->obj;
+ struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
+
+ wait_event(dev_priv->pending_flip_queue,
+ atomic_read(&obj_priv->pending_flip) == 0);
}
- ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y);
+ ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y,
+ LEAVE_ATOMIC_MODE_SET);
if (ret) {
- i915_gem_object_unpin(obj);
+ i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex);
return ret;
}
- if (old_fb) {
- intel_fb = to_intel_framebuffer(old_fb);
- obj_priv = to_intel_bo(intel_fb->obj);
- i915_gem_object_unpin(intel_fb->obj);
- }
+ if (old_fb)
+ i915_gem_object_unpin(to_intel_framebuffer(old_fb)->obj);
mutex_unlock(&dev->struct_mutex);
@@ -1615,7 +1633,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
if (!master_priv->sarea_priv)
return 0;
- if (pipe) {
+ if (intel_crtc->pipe) {
master_priv->sarea_priv->pipeB_x = x;
master_priv->sarea_priv->pipeB_y = y;
} else {
@@ -1626,7 +1644,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
}
-static void ironlake_set_pll_edp (struct drm_crtc *crtc, int clock)
+static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1659,6 +1677,7 @@ static void ironlake_set_pll_edp (struct drm_crtc *crtc, int clock)
}
I915_WRITE(DP_A, dpa_ctl);
+ POSTING_READ(DP_A);
udelay(500);
}
@@ -1669,84 +1688,109 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
- int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
- int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
- int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
- u32 temp, tries = 0;
+ u32 reg, temp, tries;
/* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
for train result */
- temp = I915_READ(fdi_rx_imr_reg);
+ reg = FDI_RX_IMR(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_RX_SYMBOL_LOCK;
temp &= ~FDI_RX_BIT_LOCK;
- I915_WRITE(fdi_rx_imr_reg, temp);
- I915_READ(fdi_rx_imr_reg);
+ I915_WRITE(reg, temp);
+ I915_READ(reg);
udelay(150);
/* enable CPU FDI TX and PCH FDI RX */
- temp = I915_READ(fdi_tx_reg);
- temp |= FDI_TX_ENABLE;
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~(7 << 19);
temp |= (intel_crtc->fdi_lanes - 1) << 19;
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_1;
- I915_WRITE(fdi_tx_reg, temp);
- I915_READ(fdi_tx_reg);
+ I915_WRITE(reg, temp | FDI_TX_ENABLE);
- temp = I915_READ(fdi_rx_reg);
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_1;
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
- I915_READ(fdi_rx_reg);
+ I915_WRITE(reg, temp | FDI_RX_ENABLE);
+
+ POSTING_READ(reg);
udelay(150);
+ /* Ironlake workaround, enable clock pointer after FDI enable*/
+ I915_WRITE(FDI_RX_CHICKEN(pipe), FDI_RX_PHASE_SYNC_POINTER_ENABLE);
+
+ reg = FDI_RX_IIR(pipe);
for (tries = 0; tries < 5; tries++) {
- temp = I915_READ(fdi_rx_iir_reg);
+ temp = I915_READ(reg);
DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
if ((temp & FDI_RX_BIT_LOCK)) {
DRM_DEBUG_KMS("FDI train 1 done.\n");
- I915_WRITE(fdi_rx_iir_reg,
- temp | FDI_RX_BIT_LOCK);
+ I915_WRITE(reg, temp | FDI_RX_BIT_LOCK);
break;
}
}
if (tries == 5)
- DRM_DEBUG_KMS("FDI train 1 fail!\n");
+ DRM_ERROR("FDI train 1 fail!\n");
/* Train 2 */
- temp = I915_READ(fdi_tx_reg);
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_2;
- I915_WRITE(fdi_tx_reg, temp);
+ I915_WRITE(reg, temp);
- temp = I915_READ(fdi_rx_reg);
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_2;
- I915_WRITE(fdi_rx_reg, temp);
- udelay(150);
+ I915_WRITE(reg, temp);
- tries = 0;
+ POSTING_READ(reg);
+ udelay(150);
+ reg = FDI_RX_IIR(pipe);
for (tries = 0; tries < 5; tries++) {
- temp = I915_READ(fdi_rx_iir_reg);
+ temp = I915_READ(reg);
DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
if (temp & FDI_RX_SYMBOL_LOCK) {
- I915_WRITE(fdi_rx_iir_reg,
- temp | FDI_RX_SYMBOL_LOCK);
+ I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK);
DRM_DEBUG_KMS("FDI train 2 done.\n");
break;
}
}
if (tries == 5)
- DRM_DEBUG_KMS("FDI train 2 fail!\n");
+ DRM_ERROR("FDI train 2 fail!\n");
DRM_DEBUG_KMS("FDI train done\n");
+
+ /* enable normal train */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
+ I915_WRITE(reg, temp);
+
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ if (HAS_PCH_CPT(dev)) {
+ temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
+ temp |= FDI_LINK_TRAIN_NORMAL_CPT;
+ } else {
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_NONE;
+ }
+ I915_WRITE(reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
+
+ /* wait one idle pattern time */
+ POSTING_READ(reg);
+ udelay(1000);
}
-static int snb_b_fdi_train_param [] = {
+static const int const snb_b_fdi_train_param [] = {
FDI_LINK_TRAIN_400MV_0DB_SNB_B,
FDI_LINK_TRAIN_400MV_6DB_SNB_B,
FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
@@ -1760,24 +1804,22 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
- int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
- int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
- int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
- u32 temp, i;
+ u32 reg, temp, i;
/* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
for train result */
- temp = I915_READ(fdi_rx_imr_reg);
+ reg = FDI_RX_IMR(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_RX_SYMBOL_LOCK;
temp &= ~FDI_RX_BIT_LOCK;
- I915_WRITE(fdi_rx_imr_reg, temp);
- I915_READ(fdi_rx_imr_reg);
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
udelay(150);
/* enable CPU FDI TX and PCH FDI RX */
- temp = I915_READ(fdi_tx_reg);
- temp |= FDI_TX_ENABLE;
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~(7 << 19);
temp |= (intel_crtc->fdi_lanes - 1) << 19;
temp &= ~FDI_LINK_TRAIN_NONE;
@@ -1785,10 +1827,10 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
/* SNB-B */
temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
- I915_WRITE(fdi_tx_reg, temp);
- I915_READ(fdi_tx_reg);
+ I915_WRITE(reg, temp | FDI_TX_ENABLE);
- temp = I915_READ(fdi_rx_reg);
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
if (HAS_PCH_CPT(dev)) {
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
@@ -1796,32 +1838,37 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_1;
}
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
- I915_READ(fdi_rx_reg);
+ I915_WRITE(reg, temp | FDI_RX_ENABLE);
+
+ POSTING_READ(reg);
udelay(150);
for (i = 0; i < 4; i++ ) {
- temp = I915_READ(fdi_tx_reg);
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
temp |= snb_b_fdi_train_param[i];
- I915_WRITE(fdi_tx_reg, temp);
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
udelay(500);
- temp = I915_READ(fdi_rx_iir_reg);
+ reg = FDI_RX_IIR(pipe);
+ temp = I915_READ(reg);
DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
if (temp & FDI_RX_BIT_LOCK) {
- I915_WRITE(fdi_rx_iir_reg,
- temp | FDI_RX_BIT_LOCK);
+ I915_WRITE(reg, temp | FDI_RX_BIT_LOCK);
DRM_DEBUG_KMS("FDI train 1 done.\n");
break;
}
}
if (i == 4)
- DRM_DEBUG_KMS("FDI train 1 fail!\n");
+ DRM_ERROR("FDI train 1 fail!\n");
/* Train 2 */
- temp = I915_READ(fdi_tx_reg);
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_2;
if (IS_GEN6(dev)) {
@@ -1829,9 +1876,10 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
/* SNB-B */
temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
}
- I915_WRITE(fdi_tx_reg, temp);
+ I915_WRITE(reg, temp);
- temp = I915_READ(fdi_rx_reg);
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
if (HAS_PCH_CPT(dev)) {
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
temp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
@@ -1839,535 +1887,593 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_2;
}
- I915_WRITE(fdi_rx_reg, temp);
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
udelay(150);
for (i = 0; i < 4; i++ ) {
- temp = I915_READ(fdi_tx_reg);
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
temp |= snb_b_fdi_train_param[i];
- I915_WRITE(fdi_tx_reg, temp);
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
udelay(500);
- temp = I915_READ(fdi_rx_iir_reg);
+ reg = FDI_RX_IIR(pipe);
+ temp = I915_READ(reg);
DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
if (temp & FDI_RX_SYMBOL_LOCK) {
- I915_WRITE(fdi_rx_iir_reg,
- temp | FDI_RX_SYMBOL_LOCK);
+ I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK);
DRM_DEBUG_KMS("FDI train 2 done.\n");
break;
}
}
if (i == 4)
- DRM_DEBUG_KMS("FDI train 2 fail!\n");
+ DRM_ERROR("FDI train 2 fail!\n");
DRM_DEBUG_KMS("FDI train done.\n");
}
-static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void ironlake_fdi_enable(struct drm_crtc *crtc)
{
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;
- int plane = intel_crtc->plane;
- int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
- int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
- int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
- int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
- int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
- int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
- int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
- int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
- int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
- int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
- int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
- int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
- int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
- int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B;
- int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B;
- int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B;
- int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B;
- int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
- int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
- int trans_dpll_sel = (pipe == 0) ? 0 : 1;
- u32 temp;
- u32 pipe_bpc;
-
- temp = I915_READ(pipeconf_reg);
- pipe_bpc = temp & PIPE_BPC_MASK;
+ u32 reg, temp;
- /* 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:
- DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane);
+ /* Write the TU size bits so error detection works */
+ I915_WRITE(FDI_RX_TUSIZE1(pipe),
+ I915_READ(PIPE_DATA_M1(pipe)) & TU_SIZE_MASK);
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
- temp = I915_READ(PCH_LVDS);
- if ((temp & LVDS_PORT_EN) == 0) {
- I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
- POSTING_READ(PCH_LVDS);
- }
- }
+ /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~((0x7 << 19) | (0x7 << 16));
+ temp |= (intel_crtc->fdi_lanes - 1) << 19;
+ temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+ I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE);
- if (!HAS_eDP) {
+ POSTING_READ(reg);
+ udelay(200);
- /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
- temp = I915_READ(fdi_rx_reg);
- /*
- * make the BPC in FDI Rx be consistent with that in
- * pipeconf reg.
- */
- temp &= ~(0x7 << 16);
- temp |= (pipe_bpc << 11);
- temp &= ~(7 << 19);
- temp |= (intel_crtc->fdi_lanes - 1) << 19;
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
- I915_READ(fdi_rx_reg);
- udelay(200);
+ /* Switch from Rawclk to PCDclk */
+ temp = I915_READ(reg);
+ I915_WRITE(reg, temp | FDI_PCDCLK);
- /* Switch from Rawclk to PCDclk */
- temp = I915_READ(fdi_rx_reg);
- I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK);
- I915_READ(fdi_rx_reg);
- udelay(200);
+ POSTING_READ(reg);
+ udelay(200);
- /* Enable CPU FDI TX PLL, always on for Ironlake */
- temp = I915_READ(fdi_tx_reg);
- if ((temp & FDI_TX_PLL_ENABLE) == 0) {
- I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
- I915_READ(fdi_tx_reg);
- udelay(100);
- }
- }
+ /* Enable CPU FDI TX PLL, always on for Ironlake */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+ I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE);
- /* Enable panel fitting for LVDS */
- if (dev_priv->pch_pf_size &&
- (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)
- || HAS_eDP || intel_pch_has_edp(crtc))) {
- /* Force use of hard-coded filter coefficients
- * as some pre-programmed values are broken,
- * e.g. x201.
- */
- I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1,
- PF_ENABLE | PF_FILTER_MED_3x3);
- I915_WRITE(pipe ? PFB_WIN_POS : PFA_WIN_POS,
- dev_priv->pch_pf_pos);
- I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ,
- dev_priv->pch_pf_size);
- }
+ POSTING_READ(reg);
+ udelay(100);
+ }
+}
- /* Enable CPU pipe */
- temp = I915_READ(pipeconf_reg);
- if ((temp & PIPEACONF_ENABLE) == 0) {
- I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
- I915_READ(pipeconf_reg);
- udelay(100);
- }
+static void intel_flush_display_plane(struct drm_device *dev,
+ int plane)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg = DSPADDR(plane);
+ I915_WRITE(reg, I915_READ(reg));
+}
- /* configure and enable CPU plane */
- temp = I915_READ(dspcntr_reg);
- if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
- I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
- /* Flush the plane changes */
- I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
- }
+/*
+ * When we disable a pipe, we need to clear any pending scanline wait events
+ * to avoid hanging the ring, which we assume we are waiting on.
+ */
+static void intel_clear_scanline_wait(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 tmp;
- if (!HAS_eDP) {
- /* For PCH output, training FDI link */
- if (IS_GEN6(dev))
- gen6_fdi_link_train(crtc);
- else
- ironlake_fdi_link_train(crtc);
+ if (IS_GEN2(dev))
+ /* Can't break the hang on i8xx */
+ return;
- /* enable PCH DPLL */
- temp = I915_READ(pch_dpll_reg);
- if ((temp & DPLL_VCO_ENABLE) == 0) {
- I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
- I915_READ(pch_dpll_reg);
- }
- udelay(200);
+ tmp = I915_READ(PRB0_CTL);
+ if (tmp & RING_WAIT) {
+ I915_WRITE(PRB0_CTL, tmp);
+ POSTING_READ(PRB0_CTL);
+ }
+}
- if (HAS_PCH_CPT(dev)) {
- /* Be sure PCH DPLL SEL is set */
- temp = I915_READ(PCH_DPLL_SEL);
- if (trans_dpll_sel == 0 &&
- (temp & TRANSA_DPLL_ENABLE) == 0)
- temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
- else if (trans_dpll_sel == 1 &&
- (temp & TRANSB_DPLL_ENABLE) == 0)
- temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
- I915_WRITE(PCH_DPLL_SEL, temp);
- I915_READ(PCH_DPLL_SEL);
- }
+static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
+{
+ struct drm_i915_gem_object *obj_priv;
+ struct drm_i915_private *dev_priv;
- /* set transcoder timing */
- I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
- I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
- I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
-
- I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
- I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
- I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
-
- /* enable normal train */
- temp = I915_READ(fdi_tx_reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
- FDI_TX_ENHANCE_FRAME_ENABLE);
- I915_READ(fdi_tx_reg);
-
- temp = I915_READ(fdi_rx_reg);
- if (HAS_PCH_CPT(dev)) {
- temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
- temp |= FDI_LINK_TRAIN_NORMAL_CPT;
- } else {
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_NONE;
- }
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
- I915_READ(fdi_rx_reg);
+ if (crtc->fb == NULL)
+ return;
- /* wait one idle pattern time */
- udelay(100);
+ obj_priv = to_intel_bo(to_intel_framebuffer(crtc->fb)->obj);
+ dev_priv = crtc->dev->dev_private;
+ wait_event(dev_priv->pending_flip_queue,
+ atomic_read(&obj_priv->pending_flip) == 0);
+}
- /* For PCH DP, enable TRANS_DP_CTL */
- if (HAS_PCH_CPT(dev) &&
- intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
- int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
- int reg;
-
- reg = I915_READ(trans_dp_ctl);
- reg &= ~(TRANS_DP_PORT_SEL_MASK |
- TRANS_DP_SYNC_MASK);
- reg |= (TRANS_DP_OUTPUT_ENABLE |
- TRANS_DP_ENH_FRAMING);
-
- if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
- reg |= TRANS_DP_HSYNC_ACTIVE_HIGH;
- if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
- reg |= TRANS_DP_VSYNC_ACTIVE_HIGH;
-
- switch (intel_trans_dp_port_sel(crtc)) {
- case PCH_DP_B:
- reg |= TRANS_DP_PORT_SEL_B;
- break;
- case PCH_DP_C:
- reg |= TRANS_DP_PORT_SEL_C;
- break;
- case PCH_DP_D:
- reg |= TRANS_DP_PORT_SEL_D;
- break;
- default:
- DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n");
- reg |= TRANS_DP_PORT_SEL_B;
- break;
- }
+static void ironlake_crtc_enable(struct drm_crtc *crtc)
+{
+ 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;
+ int plane = intel_crtc->plane;
+ u32 reg, temp;
- I915_WRITE(trans_dp_ctl, reg);
- POSTING_READ(trans_dp_ctl);
- }
+ if (intel_crtc->active)
+ return;
- /* enable PCH transcoder */
- temp = I915_READ(transconf_reg);
- /*
- * make the BPC in transcoder be consistent with
- * that in pipeconf reg.
- */
- temp &= ~PIPE_BPC_MASK;
- temp |= pipe_bpc;
- I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
- I915_READ(transconf_reg);
+ intel_crtc->active = true;
+ intel_update_watermarks(dev);
- if (wait_for(I915_READ(transconf_reg) & TRANS_STATE_ENABLE, 100, 1))
- DRM_ERROR("failed to enable transcoder\n");
- }
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ temp = I915_READ(PCH_LVDS);
+ if ((temp & LVDS_PORT_EN) == 0)
+ I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
+ }
- intel_crtc_load_lut(crtc);
+ ironlake_fdi_enable(crtc);
- intel_update_fbc(crtc, &crtc->mode);
- break;
+ /* Enable panel fitting for LVDS */
+ if (dev_priv->pch_pf_size &&
+ (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) || HAS_eDP)) {
+ /* Force use of hard-coded filter coefficients
+ * as some pre-programmed values are broken,
+ * e.g. x201.
+ */
+ I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1,
+ PF_ENABLE | PF_FILTER_MED_3x3);
+ I915_WRITE(pipe ? PFB_WIN_POS : PFA_WIN_POS,
+ dev_priv->pch_pf_pos);
+ I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ,
+ dev_priv->pch_pf_size);
+ }
+
+ /* Enable CPU pipe */
+ reg = PIPECONF(pipe);
+ temp = I915_READ(reg);
+ if ((temp & PIPECONF_ENABLE) == 0) {
+ I915_WRITE(reg, temp | PIPECONF_ENABLE);
+ POSTING_READ(reg);
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+ }
+
+ /* configure and enable CPU plane */
+ reg = DSPCNTR(plane);
+ temp = I915_READ(reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ I915_WRITE(reg, temp | DISPLAY_PLANE_ENABLE);
+ intel_flush_display_plane(dev, plane);
+ }
+
+ /* For PCH output, training FDI link */
+ if (IS_GEN6(dev))
+ gen6_fdi_link_train(crtc);
+ else
+ ironlake_fdi_link_train(crtc);
+
+ /* enable PCH DPLL */
+ reg = PCH_DPLL(pipe);
+ temp = I915_READ(reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(reg, temp | DPLL_VCO_ENABLE);
+ POSTING_READ(reg);
+ udelay(200);
+ }
- case DRM_MODE_DPMS_OFF:
- DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane);
+ if (HAS_PCH_CPT(dev)) {
+ /* Be sure PCH DPLL SEL is set */
+ temp = I915_READ(PCH_DPLL_SEL);
+ if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0)
+ temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
+ else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0)
+ temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
+ I915_WRITE(PCH_DPLL_SEL, temp);
+ }
- drm_vblank_off(dev, pipe);
- /* Disable display plane */
- temp = I915_READ(dspcntr_reg);
- if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
- I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
- /* Flush the plane changes */
- I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
- I915_READ(dspbase_reg);
+ /* set transcoder timing */
+ I915_WRITE(TRANS_HTOTAL(pipe), I915_READ(HTOTAL(pipe)));
+ I915_WRITE(TRANS_HBLANK(pipe), I915_READ(HBLANK(pipe)));
+ I915_WRITE(TRANS_HSYNC(pipe), I915_READ(HSYNC(pipe)));
+
+ 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)));
+
+ /* For PCH DP, enable TRANS_DP_CTL */
+ if (HAS_PCH_CPT(dev) &&
+ intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+ reg = TRANS_DP_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~(TRANS_DP_PORT_SEL_MASK |
+ TRANS_DP_SYNC_MASK);
+ temp |= (TRANS_DP_OUTPUT_ENABLE |
+ TRANS_DP_ENH_FRAMING);
+
+ if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
+ temp |= TRANS_DP_HSYNC_ACTIVE_HIGH;
+ if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
+ temp |= TRANS_DP_VSYNC_ACTIVE_HIGH;
+
+ switch (intel_trans_dp_port_sel(crtc)) {
+ case PCH_DP_B:
+ temp |= TRANS_DP_PORT_SEL_B;
+ break;
+ case PCH_DP_C:
+ temp |= TRANS_DP_PORT_SEL_C;
+ break;
+ case PCH_DP_D:
+ temp |= TRANS_DP_PORT_SEL_D;
+ break;
+ default:
+ DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n");
+ temp |= TRANS_DP_PORT_SEL_B;
+ break;
}
- if (dev_priv->cfb_plane == plane &&
- dev_priv->display.disable_fbc)
- dev_priv->display.disable_fbc(dev);
+ I915_WRITE(reg, temp);
+ }
- /* disable cpu pipe, disable after all planes disabled */
- temp = I915_READ(pipeconf_reg);
- if ((temp & PIPEACONF_ENABLE) != 0) {
- I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+ /* enable PCH transcoder */
+ reg = TRANSCONF(pipe);
+ temp = I915_READ(reg);
+ /*
+ * make the BPC in transcoder be consistent with
+ * that in pipeconf reg.
+ */
+ temp &= ~PIPE_BPC_MASK;
+ temp |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
+ I915_WRITE(reg, temp | TRANS_ENABLE);
+ if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
+ DRM_ERROR("failed to enable transcoder %d\n", pipe);
- /* wait for cpu pipe off, pipe state */
- if (wait_for((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) == 0, 50, 1))
- DRM_ERROR("failed to turn off cpu pipe\n");
- } else
- DRM_DEBUG_KMS("crtc %d is disabled\n", pipe);
+ intel_crtc_load_lut(crtc);
+ intel_update_fbc(dev);
+ intel_crtc_update_cursor(crtc, true);
+}
- udelay(100);
+static void ironlake_crtc_disable(struct drm_crtc *crtc)
+{
+ 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;
+ int plane = intel_crtc->plane;
+ u32 reg, temp;
+
+ if (!intel_crtc->active)
+ return;
- /* Disable PF */
- I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, 0);
- I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, 0);
+ intel_crtc_wait_for_pending_flips(crtc);
+ drm_vblank_off(dev, pipe);
+ intel_crtc_update_cursor(crtc, false);
- /* disable CPU FDI tx and PCH FDI rx */
- temp = I915_READ(fdi_tx_reg);
- I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
- I915_READ(fdi_tx_reg);
+ /* Disable display plane */
+ reg = DSPCNTR(plane);
+ temp = I915_READ(reg);
+ if (temp & DISPLAY_PLANE_ENABLE) {
+ I915_WRITE(reg, temp & ~DISPLAY_PLANE_ENABLE);
+ intel_flush_display_plane(dev, plane);
+ }
- temp = I915_READ(fdi_rx_reg);
- /* BPC in FDI rx is consistent with that in pipeconf */
- temp &= ~(0x07 << 16);
- temp |= (pipe_bpc << 11);
- I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
- I915_READ(fdi_rx_reg);
+ if (dev_priv->cfb_plane == plane &&
+ dev_priv->display.disable_fbc)
+ dev_priv->display.disable_fbc(dev);
- udelay(100);
+ /* disable cpu pipe, disable after all planes disabled */
+ reg = PIPECONF(pipe);
+ temp = I915_READ(reg);
+ if (temp & PIPECONF_ENABLE) {
+ I915_WRITE(reg, temp & ~PIPECONF_ENABLE);
+ POSTING_READ(reg);
+ /* wait for cpu pipe off, pipe state */
+ intel_wait_for_pipe_off(dev, intel_crtc->pipe);
+ }
+
+ /* Disable PF */
+ I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, 0);
+ I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, 0);
+
+ /* disable CPU FDI tx and PCH FDI rx */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ I915_WRITE(reg, temp & ~FDI_TX_ENABLE);
+ POSTING_READ(reg);
+
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~(0x7 << 16);
+ temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+ I915_WRITE(reg, temp & ~FDI_RX_ENABLE);
+
+ POSTING_READ(reg);
+ udelay(100);
+
+ /* Ironlake workaround, disable clock pointer after downing FDI */
+ I915_WRITE(FDI_RX_CHICKEN(pipe),
+ I915_READ(FDI_RX_CHICKEN(pipe) &
+ ~FDI_RX_PHASE_SYNC_POINTER_ENABLE));
+
+ /* still set train pattern 1 */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_1;
+ I915_WRITE(reg, temp);
- /* still set train pattern 1 */
- temp = I915_READ(fdi_tx_reg);
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ if (HAS_PCH_CPT(dev)) {
+ temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
+ temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
+ } else {
temp &= ~FDI_LINK_TRAIN_NONE;
temp |= FDI_LINK_TRAIN_PATTERN_1;
- I915_WRITE(fdi_tx_reg, temp);
- POSTING_READ(fdi_tx_reg);
-
- temp = I915_READ(fdi_rx_reg);
- if (HAS_PCH_CPT(dev)) {
- temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
- temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
- } else {
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_PATTERN_1;
- }
- I915_WRITE(fdi_rx_reg, temp);
- POSTING_READ(fdi_rx_reg);
+ }
+ /* BPC in FDI rx is consistent with that in PIPECONF */
+ temp &= ~(0x07 << 16);
+ temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+ I915_WRITE(reg, temp);
- udelay(100);
+ POSTING_READ(reg);
+ udelay(100);
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
- temp = I915_READ(PCH_LVDS);
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ temp = I915_READ(PCH_LVDS);
+ if (temp & LVDS_PORT_EN) {
I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
- I915_READ(PCH_LVDS);
+ POSTING_READ(PCH_LVDS);
udelay(100);
}
+ }
- /* disable PCH transcoder */
- temp = I915_READ(transconf_reg);
- if ((temp & TRANS_ENABLE) != 0) {
- I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
+ /* disable PCH transcoder */
+ reg = TRANSCONF(plane);
+ temp = I915_READ(reg);
+ if (temp & TRANS_ENABLE) {
+ I915_WRITE(reg, temp & ~TRANS_ENABLE);
+ /* wait for PCH transcoder off, transcoder state */
+ if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
+ DRM_ERROR("failed to disable transcoder\n");
+ }
- /* wait for PCH transcoder off, transcoder state */
- if (wait_for((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0, 50, 1))
- DRM_ERROR("failed to disable transcoder\n");
- }
+ if (HAS_PCH_CPT(dev)) {
+ /* disable TRANS_DP_CTL */
+ reg = TRANS_DP_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK);
+ I915_WRITE(reg, temp);
- temp = I915_READ(transconf_reg);
- /* BPC in transcoder is consistent with that in pipeconf */
- temp &= ~PIPE_BPC_MASK;
- temp |= pipe_bpc;
- I915_WRITE(transconf_reg, temp);
- I915_READ(transconf_reg);
- udelay(100);
+ /* disable DPLL_SEL */
+ temp = I915_READ(PCH_DPLL_SEL);
+ if (pipe == 0)
+ temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
+ else
+ temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
+ I915_WRITE(PCH_DPLL_SEL, temp);
+ }
- if (HAS_PCH_CPT(dev)) {
- /* disable TRANS_DP_CTL */
- int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
- int reg;
+ /* disable PCH DPLL */
+ reg = PCH_DPLL(pipe);
+ temp = I915_READ(reg);
+ I915_WRITE(reg, temp & ~DPLL_VCO_ENABLE);
- reg = I915_READ(trans_dp_ctl);
- reg &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK);
- I915_WRITE(trans_dp_ctl, reg);
- POSTING_READ(trans_dp_ctl);
+ /* Switch from PCDclk to Rawclk */
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ I915_WRITE(reg, temp & ~FDI_PCDCLK);
- /* disable DPLL_SEL */
- temp = I915_READ(PCH_DPLL_SEL);
- if (trans_dpll_sel == 0)
- temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
- else
- temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
- I915_WRITE(PCH_DPLL_SEL, temp);
- I915_READ(PCH_DPLL_SEL);
+ /* Disable CPU FDI TX PLL */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE);
- }
+ POSTING_READ(reg);
+ udelay(100);
- /* disable PCH DPLL */
- temp = I915_READ(pch_dpll_reg);
- I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
- I915_READ(pch_dpll_reg);
-
- /* Switch from PCDclk to Rawclk */
- temp = I915_READ(fdi_rx_reg);
- temp &= ~FDI_SEL_PCDCLK;
- I915_WRITE(fdi_rx_reg, temp);
- I915_READ(fdi_rx_reg);
-
- /* Disable CPU FDI TX PLL */
- temp = I915_READ(fdi_tx_reg);
- I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE);
- I915_READ(fdi_tx_reg);
- udelay(100);
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE);
- temp = I915_READ(fdi_rx_reg);
- temp &= ~FDI_RX_PLL_ENABLE;
- I915_WRITE(fdi_rx_reg, temp);
- I915_READ(fdi_rx_reg);
+ /* Wait for the clocks to turn off. */
+ POSTING_READ(reg);
+ udelay(100);
- /* Wait for the clocks to turn off. */
- udelay(100);
+ intel_crtc->active = false;
+ intel_update_watermarks(dev);
+ intel_update_fbc(dev);
+ intel_clear_scanline_wait(dev);
+}
+
+static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int plane = intel_crtc->plane;
+
+ /* 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:
+ DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane);
+ ironlake_crtc_enable(crtc);
+ break;
+
+ case DRM_MODE_DPMS_OFF:
+ DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane);
+ ironlake_crtc_disable(crtc);
break;
}
}
static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
{
- struct intel_overlay *overlay;
- int ret;
-
if (!enable && intel_crtc->overlay) {
- overlay = intel_crtc->overlay;
- mutex_lock(&overlay->dev->struct_mutex);
- for (;;) {
- ret = intel_overlay_switch_off(overlay);
- if (ret == 0)
- break;
+ struct drm_device *dev = intel_crtc->base.dev;
- ret = intel_overlay_recover_from_interrupt(overlay, 0);
- if (ret != 0) {
- /* overlay doesn't react anymore. Usually
- * results in a black screen and an unkillable
- * X server. */
- BUG();
- overlay->hw_wedged = HW_WEDGED;
- break;
- }
- }
- mutex_unlock(&overlay->dev->struct_mutex);
+ mutex_lock(&dev->struct_mutex);
+ (void) intel_overlay_switch_off(intel_crtc->overlay, false);
+ mutex_unlock(&dev->struct_mutex);
}
- /* Let userspace switch the overlay on again. In most cases userspace
- * has to recompute where to put it anyway. */
- return;
+ /* Let userspace switch the overlay on again. In most cases userspace
+ * has to recompute where to put it anyway.
+ */
}
-static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void i9xx_crtc_enable(struct drm_crtc *crtc)
{
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;
int plane = intel_crtc->plane;
- int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
- int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
- int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
- int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
- u32 temp;
+ u32 reg, temp;
- /* 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 = I915_READ(dpll_reg);
- if ((temp & DPLL_VCO_ENABLE) == 0) {
- I915_WRITE(dpll_reg, temp);
- I915_READ(dpll_reg);
- /* Wait for the clocks to stabilize. */
- udelay(150);
- I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
- I915_READ(dpll_reg);
- /* Wait for the clocks to stabilize. */
- udelay(150);
- I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
- I915_READ(dpll_reg);
- /* Wait for the clocks to stabilize. */
- udelay(150);
- }
+ if (intel_crtc->active)
+ return;
- /* Enable the pipe */
- temp = I915_READ(pipeconf_reg);
- if ((temp & PIPEACONF_ENABLE) == 0)
- I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
-
- /* Enable the plane */
- temp = I915_READ(dspcntr_reg);
- if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
- I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
- /* Flush the plane changes */
- I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
- }
+ intel_crtc->active = true;
+ intel_update_watermarks(dev);
- intel_crtc_load_lut(crtc);
+ /* Enable the DPLL */
+ reg = DPLL(pipe);
+ temp = I915_READ(reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(reg, temp);
+
+ /* Wait for the clocks to stabilize. */
+ POSTING_READ(reg);
+ udelay(150);
- if ((IS_I965G(dev) || plane == 0))
- intel_update_fbc(crtc, &crtc->mode);
+ I915_WRITE(reg, temp | DPLL_VCO_ENABLE);
- /* Give the overlay scaler a chance to enable if it's on this pipe */
- intel_crtc_dpms_overlay(intel_crtc, true);
- break;
- case DRM_MODE_DPMS_OFF:
- /* Give the overlay scaler a chance to disable if it's on this pipe */
- intel_crtc_dpms_overlay(intel_crtc, false);
- drm_vblank_off(dev, pipe);
-
- if (dev_priv->cfb_plane == plane &&
- dev_priv->display.disable_fbc)
- dev_priv->display.disable_fbc(dev);
-
- /* Disable display plane */
- temp = I915_READ(dspcntr_reg);
- if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
- I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
- /* Flush the plane changes */
- I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
- I915_READ(dspbase_reg);
- }
+ /* Wait for the clocks to stabilize. */
+ POSTING_READ(reg);
+ udelay(150);
- /* Don't disable pipe A or pipe A PLLs if needed */
- if (pipeconf_reg == PIPEACONF &&
- (dev_priv->quirks & QUIRK_PIPEA_FORCE)) {
- /* Wait for vblank for the disable to take effect */
+ I915_WRITE(reg, temp | DPLL_VCO_ENABLE);
+
+ /* Wait for the clocks to stabilize. */
+ POSTING_READ(reg);
+ udelay(150);
+ }
+
+ /* Enable the pipe */
+ reg = PIPECONF(pipe);
+ temp = I915_READ(reg);
+ if ((temp & PIPECONF_ENABLE) == 0)
+ I915_WRITE(reg, temp | PIPECONF_ENABLE);
+
+ /* Enable the plane */
+ reg = DSPCNTR(plane);
+ temp = I915_READ(reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ I915_WRITE(reg, temp | DISPLAY_PLANE_ENABLE);
+ intel_flush_display_plane(dev, plane);
+ }
+
+ intel_crtc_load_lut(crtc);
+ intel_update_fbc(dev);
+
+ /* Give the overlay scaler a chance to enable if it's on this pipe */
+ intel_crtc_dpms_overlay(intel_crtc, true);
+ intel_crtc_update_cursor(crtc, true);
+}
+
+static void i9xx_crtc_disable(struct drm_crtc *crtc)
+{
+ 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;
+ int plane = intel_crtc->plane;
+ u32 reg, temp;
+
+ if (!intel_crtc->active)
+ return;
+
+ /* Give the overlay scaler a chance to disable if it's on this pipe */
+ intel_crtc_wait_for_pending_flips(crtc);
+ drm_vblank_off(dev, pipe);
+ intel_crtc_dpms_overlay(intel_crtc, false);
+ intel_crtc_update_cursor(crtc, false);
+
+ if (dev_priv->cfb_plane == plane &&
+ dev_priv->display.disable_fbc)
+ dev_priv->display.disable_fbc(dev);
+
+ /* Disable display plane */
+ reg = DSPCNTR(plane);
+ temp = I915_READ(reg);
+ if (temp & DISPLAY_PLANE_ENABLE) {
+ I915_WRITE(reg, temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ intel_flush_display_plane(dev, plane);
+
+ /* Wait for vblank for the disable to take effect */
+ if (IS_GEN2(dev))
intel_wait_for_vblank(dev, pipe);
- goto skip_pipe_off;
- }
+ }
- /* Next, disable display pipes */
- temp = I915_READ(pipeconf_reg);
- if ((temp & PIPEACONF_ENABLE) != 0) {
- I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
- I915_READ(pipeconf_reg);
- }
+ /* Don't disable pipe A or pipe A PLLs if needed */
+ if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE))
+ goto done;
+
+ /* Next, disable display pipes */
+ reg = PIPECONF(pipe);
+ temp = I915_READ(reg);
+ if (temp & PIPECONF_ENABLE) {
+ I915_WRITE(reg, temp & ~PIPECONF_ENABLE);
/* Wait for the pipe to turn off */
+ POSTING_READ(reg);
intel_wait_for_pipe_off(dev, pipe);
+ }
+
+ reg = DPLL(pipe);
+ temp = I915_READ(reg);
+ if (temp & DPLL_VCO_ENABLE) {
+ I915_WRITE(reg, temp & ~DPLL_VCO_ENABLE);
- temp = I915_READ(dpll_reg);
- if ((temp & DPLL_VCO_ENABLE) != 0) {
- I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
- I915_READ(dpll_reg);
- }
- skip_pipe_off:
/* Wait for the clocks to turn off. */
+ POSTING_READ(reg);
udelay(150);
+ }
+
+done:
+ intel_crtc->active = false;
+ intel_update_fbc(dev);
+ intel_update_watermarks(dev);
+ intel_clear_scanline_wait(dev);
+}
+
+static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ /* 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:
+ i9xx_crtc_enable(crtc);
+ break;
+ case DRM_MODE_DPMS_OFF:
+ i9xx_crtc_disable(crtc);
break;
}
}
@@ -2388,26 +2494,9 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
return;
intel_crtc->dpms_mode = mode;
- intel_crtc->cursor_on = mode == DRM_MODE_DPMS_ON;
-
- /* When switching on the display, ensure that SR is disabled
- * with multiple pipes prior to enabling to new pipe.
- *
- * When switching off the display, make sure the cursor is
- * properly hidden prior to disabling the pipe.
- */
- if (mode == DRM_MODE_DPMS_ON)
- intel_update_watermarks(dev);
- else
- intel_crtc_update_cursor(crtc);
dev_priv->display.dpms(crtc, mode);
- if (mode == DRM_MODE_DPMS_ON)
- intel_crtc_update_cursor(crtc);
- else
- intel_update_watermarks(dev);
-
if (!dev->primary->master)
return;
@@ -2432,16 +2521,46 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
}
}
-static void intel_crtc_prepare (struct drm_crtc *crtc)
+static void intel_crtc_disable(struct drm_crtc *crtc)
{
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_device *dev = crtc->dev;
+
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+
+ if (crtc->fb) {
+ mutex_lock(&dev->struct_mutex);
+ i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
+ mutex_unlock(&dev->struct_mutex);
+ }
}
-static void intel_crtc_commit (struct drm_crtc *crtc)
+/* Prepare for a mode set.
+ *
+ * Note we could be a lot smarter here. We need to figure out which outputs
+ * will be enabled, which disabled (in short, how the config will changes)
+ * and perform the minimum necessary steps to accomplish that, e.g. updating
+ * watermarks, FBC configuration, making sure PLLs are programmed correctly,
+ * panel fitting is in the proper state, etc.
+ */
+static void i9xx_crtc_prepare(struct drm_crtc *crtc)
{
- struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
- crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+ i9xx_crtc_disable(crtc);
+}
+
+static void i9xx_crtc_commit(struct drm_crtc *crtc)
+{
+ i9xx_crtc_enable(crtc);
+}
+
+static void ironlake_crtc_prepare(struct drm_crtc *crtc)
+{
+ ironlake_crtc_disable(crtc);
+}
+
+static void ironlake_crtc_commit(struct drm_crtc *crtc)
+{
+ ironlake_crtc_enable(crtc);
}
void intel_encoder_prepare (struct drm_encoder *encoder)
@@ -2460,13 +2579,7 @@ void intel_encoder_commit (struct drm_encoder *encoder)
void intel_encoder_destroy(struct drm_encoder *encoder)
{
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
-
- if (intel_encoder->ddc_bus)
- intel_i2c_destroy(intel_encoder->ddc_bus);
-
- if (intel_encoder->i2c_bus)
- intel_i2c_destroy(intel_encoder->i2c_bus);
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
drm_encoder_cleanup(encoder);
kfree(intel_encoder);
@@ -2557,33 +2670,6 @@ static int i830_get_display_clock_speed(struct drm_device *dev)
return 133000;
}
-/**
- * Return the pipe currently connected to the panel fitter,
- * or -1 if the panel fitter is not present or not in use
- */
-int intel_panel_fitter_pipe (struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 pfit_control;
-
- /* i830 doesn't have a panel fitter */
- if (IS_I830(dev))
- return -1;
-
- pfit_control = I915_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 */
- if (IS_I965G(dev))
- return (pfit_control >> 29) & 0x3;
-
- /* older chips can only use pipe 1 */
- return 1;
-}
-
struct fdi_m_n {
u32 tu;
u32 gmch_m;
@@ -2902,7 +2988,7 @@ static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - size;
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
- plane ? "B" : "A", size);
+ plane ? "B" : "A", size);
return size;
}
@@ -2919,7 +3005,7 @@ static int i85x_get_fifo_size(struct drm_device *dev, int plane)
size >>= 1; /* Convert to cachelines */
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
- plane ? "B" : "A", size);
+ plane ? "B" : "A", size);
return size;
}
@@ -2934,8 +3020,8 @@ static int i845_get_fifo_size(struct drm_device *dev, int plane)
size >>= 2; /* Convert to cachelines */
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
- plane ? "B" : "A",
- size);
+ plane ? "B" : "A",
+ size);
return size;
}
@@ -2950,14 +3036,14 @@ static int i830_get_fifo_size(struct drm_device *dev, int plane)
size >>= 1; /* Convert to cachelines */
DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
- plane ? "B" : "A", size);
+ plane ? "B" : "A", size);
return size;
}
static void pineview_update_wm(struct drm_device *dev, int planea_clock,
- int planeb_clock, int sr_hdisplay, int unused,
- int pixel_size)
+ int planeb_clock, int sr_hdisplay, int unused,
+ int pixel_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
const struct cxsr_latency *latency;
@@ -3069,13 +3155,13 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
/* Use ns/us then divide to preserve precision */
sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
- pixel_size * sr_hdisplay;
+ pixel_size * sr_hdisplay;
sr_entries = DIV_ROUND_UP(sr_entries, cacheline_size);
entries_required = (((sr_latency_ns / line_time_us) +
1000) / 1000) * pixel_size * 64;
entries_required = DIV_ROUND_UP(entries_required,
- g4x_cursor_wm_info.cacheline_size);
+ g4x_cursor_wm_info.cacheline_size);
cursor_sr = entries_required + g4x_cursor_wm_info.guard_size;
if (cursor_sr > g4x_cursor_wm_info.max_wm)
@@ -3087,7 +3173,7 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
} else {
/* Turn off self refresh if both pipes are enabled */
I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
- & ~FW_BLC_SELF_EN);
+ & ~FW_BLC_SELF_EN);
}
DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, SR %d\n",
@@ -3125,7 +3211,7 @@ static void i965_update_wm(struct drm_device *dev, int planea_clock,
/* Use ns/us then divide to preserve precision */
sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
- pixel_size * sr_hdisplay;
+ pixel_size * sr_hdisplay;
sr_entries = DIV_ROUND_UP(sr_entries, I915_FIFO_LINE_SIZE);
DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
srwm = I965_FIFO_SIZE - sr_entries;
@@ -3134,11 +3220,11 @@ static void i965_update_wm(struct drm_device *dev, int planea_clock,
srwm &= 0x1ff;
sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
- pixel_size * 64;
+ pixel_size * 64;
sr_entries = DIV_ROUND_UP(sr_entries,
i965_cursor_wm_info.cacheline_size);
cursor_sr = i965_cursor_wm_info.fifo_size -
- (sr_entries + i965_cursor_wm_info.guard_size);
+ (sr_entries + i965_cursor_wm_info.guard_size);
if (cursor_sr > i965_cursor_wm_info.max_wm)
cursor_sr = i965_cursor_wm_info.max_wm;
@@ -3146,11 +3232,11 @@ static void i965_update_wm(struct drm_device *dev, int planea_clock,
DRM_DEBUG_KMS("self-refresh watermark: display plane %d "
"cursor %d\n", srwm, cursor_sr);
- if (IS_I965GM(dev))
+ if (IS_CRESTLINE(dev))
I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
} else {
/* Turn off self refresh if both pipes are enabled */
- if (IS_I965GM(dev))
+ if (IS_CRESTLINE(dev))
I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
& ~FW_BLC_SELF_EN);
}
@@ -3180,9 +3266,9 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
int sr_clock, sr_entries = 0;
/* Create copies of the base settings for each pipe */
- if (IS_I965GM(dev) || IS_I945GM(dev))
+ if (IS_CRESTLINE(dev) || IS_I945GM(dev))
planea_params = planeb_params = i945_wm_info;
- else if (IS_I9XX(dev))
+ else if (!IS_GEN2(dev))
planea_params = planeb_params = i915_wm_info;
else
planea_params = planeb_params = i855_wm_info;
@@ -3217,7 +3303,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
/* Use ns/us then divide to preserve precision */
sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
- pixel_size * sr_hdisplay;
+ pixel_size * sr_hdisplay;
sr_entries = DIV_ROUND_UP(sr_entries, cacheline_size);
DRM_DEBUG_KMS("self-refresh entries: %d\n", sr_entries);
srwm = total_size - sr_entries;
@@ -3242,7 +3328,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
}
DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
- planea_wm, planeb_wm, cwm, srwm);
+ planea_wm, planeb_wm, cwm, srwm);
fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f);
fwater_hi = (cwm & 0x1f);
@@ -3276,146 +3362,130 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused,
#define ILK_LP0_PLANE_LATENCY 700
#define ILK_LP0_CURSOR_LATENCY 1300
-static void ironlake_update_wm(struct drm_device *dev, int planea_clock,
- int planeb_clock, int sr_hdisplay, int sr_htotal,
- int pixel_size)
+static bool ironlake_compute_wm0(struct drm_device *dev,
+ int pipe,
+ int *plane_wm,
+ int *cursor_wm)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
- int sr_wm, cursor_wm;
- unsigned long line_time_us;
- int sr_clock, entries_required;
- u32 reg_value;
- int line_count;
- int planea_htotal = 0, planeb_htotal = 0;
struct drm_crtc *crtc;
+ int htotal, hdisplay, clock, pixel_size = 0;
+ int line_time_us, line_count, entries;
- /* Need htotal for all active display plane */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- if (intel_crtc->dpms_mode == DRM_MODE_DPMS_ON) {
- if (intel_crtc->plane == 0)
- planea_htotal = crtc->mode.htotal;
- else
- planeb_htotal = crtc->mode.htotal;
- }
- }
-
- /* Calculate and update the watermark for plane A */
- if (planea_clock) {
- entries_required = ((planea_clock / 1000) * pixel_size *
- ILK_LP0_PLANE_LATENCY) / 1000;
- entries_required = DIV_ROUND_UP(entries_required,
- ironlake_display_wm_info.cacheline_size);
- planea_wm = entries_required +
- ironlake_display_wm_info.guard_size;
-
- if (planea_wm > (int)ironlake_display_wm_info.max_wm)
- planea_wm = ironlake_display_wm_info.max_wm;
-
- /* Use the large buffer method to calculate cursor watermark */
- line_time_us = (planea_htotal * 1000) / planea_clock;
-
- /* Use ns/us then divide to preserve precision */
- line_count = (ILK_LP0_CURSOR_LATENCY / line_time_us + 1000) / 1000;
-
- /* calculate the cursor watermark for cursor A */
- entries_required = line_count * 64 * pixel_size;
- entries_required = DIV_ROUND_UP(entries_required,
- ironlake_cursor_wm_info.cacheline_size);
- cursora_wm = entries_required + ironlake_cursor_wm_info.guard_size;
- if (cursora_wm > ironlake_cursor_wm_info.max_wm)
- cursora_wm = ironlake_cursor_wm_info.max_wm;
-
- reg_value = I915_READ(WM0_PIPEA_ILK);
- reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
- reg_value |= (planea_wm << WM0_PIPE_PLANE_SHIFT) |
- (cursora_wm & WM0_PIPE_CURSOR_MASK);
- I915_WRITE(WM0_PIPEA_ILK, reg_value);
- DRM_DEBUG_KMS("FIFO watermarks For pipe A - plane %d, "
- "cursor: %d\n", planea_wm, cursora_wm);
- }
- /* Calculate and update the watermark for plane B */
- if (planeb_clock) {
- entries_required = ((planeb_clock / 1000) * pixel_size *
- ILK_LP0_PLANE_LATENCY) / 1000;
- entries_required = DIV_ROUND_UP(entries_required,
- ironlake_display_wm_info.cacheline_size);
- planeb_wm = entries_required +
- ironlake_display_wm_info.guard_size;
-
- if (planeb_wm > (int)ironlake_display_wm_info.max_wm)
- planeb_wm = ironlake_display_wm_info.max_wm;
+ crtc = intel_get_crtc_for_pipe(dev, pipe);
+ if (crtc->fb == NULL || !crtc->enabled)
+ return false;
- /* Use the large buffer method to calculate cursor watermark */
- line_time_us = (planeb_htotal * 1000) / planeb_clock;
+ htotal = crtc->mode.htotal;
+ hdisplay = crtc->mode.hdisplay;
+ clock = crtc->mode.clock;
+ pixel_size = crtc->fb->bits_per_pixel / 8;
+
+ /* Use the small buffer method to calculate plane watermark */
+ entries = ((clock * pixel_size / 1000) * ILK_LP0_PLANE_LATENCY) / 1000;
+ entries = DIV_ROUND_UP(entries,
+ ironlake_display_wm_info.cacheline_size);
+ *plane_wm = entries + ironlake_display_wm_info.guard_size;
+ if (*plane_wm > (int)ironlake_display_wm_info.max_wm)
+ *plane_wm = ironlake_display_wm_info.max_wm;
+
+ /* Use the large buffer method to calculate cursor watermark */
+ line_time_us = ((htotal * 1000) / clock);
+ line_count = (ILK_LP0_CURSOR_LATENCY / line_time_us + 1000) / 1000;
+ entries = line_count * 64 * pixel_size;
+ entries = DIV_ROUND_UP(entries,
+ ironlake_cursor_wm_info.cacheline_size);
+ *cursor_wm = entries + ironlake_cursor_wm_info.guard_size;
+ if (*cursor_wm > ironlake_cursor_wm_info.max_wm)
+ *cursor_wm = ironlake_cursor_wm_info.max_wm;
- /* Use ns/us then divide to preserve precision */
- line_count = (ILK_LP0_CURSOR_LATENCY / line_time_us + 1000) / 1000;
+ return true;
+}
- /* calculate the cursor watermark for cursor B */
- entries_required = line_count * 64 * pixel_size;
- entries_required = DIV_ROUND_UP(entries_required,
- ironlake_cursor_wm_info.cacheline_size);
- cursorb_wm = entries_required + ironlake_cursor_wm_info.guard_size;
- if (cursorb_wm > ironlake_cursor_wm_info.max_wm)
- cursorb_wm = ironlake_cursor_wm_info.max_wm;
+static void ironlake_update_wm(struct drm_device *dev,
+ int planea_clock, int planeb_clock,
+ int sr_hdisplay, int sr_htotal,
+ int pixel_size)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int plane_wm, cursor_wm, enabled;
+ int tmp;
+
+ enabled = 0;
+ if (ironlake_compute_wm0(dev, 0, &plane_wm, &cursor_wm)) {
+ I915_WRITE(WM0_PIPEA_ILK,
+ (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);
+ enabled++;
+ }
- reg_value = I915_READ(WM0_PIPEB_ILK);
- reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
- reg_value |= (planeb_wm << WM0_PIPE_PLANE_SHIFT) |
- (cursorb_wm & WM0_PIPE_CURSOR_MASK);
- I915_WRITE(WM0_PIPEB_ILK, reg_value);
- DRM_DEBUG_KMS("FIFO watermarks For pipe B - plane %d, "
- "cursor: %d\n", planeb_wm, cursorb_wm);
+ if (ironlake_compute_wm0(dev, 1, &plane_wm, &cursor_wm)) {
+ I915_WRITE(WM0_PIPEB_ILK,
+ (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);
+ enabled++;
}
/*
* Calculate and update the self-refresh watermark only when one
* display plane is used.
*/
- if (!planea_clock || !planeb_clock) {
-
+ tmp = 0;
+ if (enabled == 1 && /* XXX disabled due to buggy implmentation? */ 0) {
+ unsigned long line_time_us;
+ int small, large, plane_fbc;
+ int sr_clock, entries;
+ int line_count, line_size;
/* Read the self-refresh latency. The unit is 0.5us */
int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK;
sr_clock = planea_clock ? planea_clock : planeb_clock;
- line_time_us = ((sr_htotal * 1000) / sr_clock);
+ line_time_us = (sr_htotal * 1000) / sr_clock;
/* Use ns/us then divide to preserve precision */
line_count = ((ilk_sr_latency * 500) / line_time_us + 1000)
- / 1000;
+ / 1000;
+ line_size = sr_hdisplay * pixel_size;
- /* calculate the self-refresh watermark for display plane */
- entries_required = line_count * sr_hdisplay * pixel_size;
- entries_required = DIV_ROUND_UP(entries_required,
- ironlake_display_srwm_info.cacheline_size);
- sr_wm = entries_required +
- ironlake_display_srwm_info.guard_size;
+ /* Use the minimum of the small and large buffer method for primary */
+ small = ((sr_clock * pixel_size / 1000) * (ilk_sr_latency * 500)) / 1000;
+ large = line_count * line_size;
- /* calculate the self-refresh watermark for display cursor */
- entries_required = line_count * pixel_size * 64;
- entries_required = DIV_ROUND_UP(entries_required,
- ironlake_cursor_srwm_info.cacheline_size);
- cursor_wm = entries_required +
- ironlake_cursor_srwm_info.guard_size;
+ entries = DIV_ROUND_UP(min(small, large),
+ ironlake_display_srwm_info.cacheline_size);
- /* configure watermark and enable self-refresh */
- reg_value = I915_READ(WM1_LP_ILK);
- reg_value &= ~(WM1_LP_LATENCY_MASK | WM1_LP_SR_MASK |
- WM1_LP_CURSOR_MASK);
- reg_value |= (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) |
- (sr_wm << WM1_LP_SR_SHIFT) | cursor_wm;
+ plane_fbc = entries * 64;
+ plane_fbc = DIV_ROUND_UP(plane_fbc, line_size);
- I915_WRITE(WM1_LP_ILK, reg_value);
- DRM_DEBUG_KMS("self-refresh watermark: display plane %d "
- "cursor %d\n", sr_wm, cursor_wm);
+ plane_wm = entries + ironlake_display_srwm_info.guard_size;
+ if (plane_wm > (int)ironlake_display_srwm_info.max_wm)
+ plane_wm = ironlake_display_srwm_info.max_wm;
- } else {
- /* Turn off self refresh if both pipes are enabled */
- I915_WRITE(WM1_LP_ILK, I915_READ(WM1_LP_ILK) & ~WM1_LP_SR_EN);
- }
+ /* calculate the self-refresh watermark for display cursor */
+ entries = line_count * pixel_size * 64;
+ entries = DIV_ROUND_UP(entries,
+ ironlake_cursor_srwm_info.cacheline_size);
+
+ cursor_wm = entries + ironlake_cursor_srwm_info.guard_size;
+ if (cursor_wm > (int)ironlake_cursor_srwm_info.max_wm)
+ cursor_wm = ironlake_cursor_srwm_info.max_wm;
+
+ /* configure watermark and enable self-refresh */
+ tmp = (WM1_LP_SR_EN |
+ (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) |
+ (plane_fbc << WM1_LP_FBC_SHIFT) |
+ (plane_wm << WM1_LP_SR_SHIFT) |
+ cursor_wm);
+ DRM_DEBUG_KMS("self-refresh watermark: display plane %d, fbc lines %d,"
+ " cursor %d\n", plane_wm, plane_fbc, cursor_wm);
+ }
+ I915_WRITE(WM1_LP_ILK, tmp);
+ /* XXX setup WM2 and WM3 */
}
+
/**
* intel_update_watermarks - update FIFO watermark values based on current modes
*
@@ -3447,7 +3517,7 @@ static void ironlake_update_wm(struct drm_device *dev, int planea_clock,
*
* We don't use the sprite, so we can ignore that. And on Crestline we have
* to set the non-SR watermarks to 8.
- */
+ */
static void intel_update_watermarks(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3463,15 +3533,15 @@ static void intel_update_watermarks(struct drm_device *dev)
/* Get the clock config from both planes */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- if (intel_crtc->dpms_mode == DRM_MODE_DPMS_ON) {
+ if (intel_crtc->active) {
enabled++;
if (intel_crtc->plane == 0) {
DRM_DEBUG_KMS("plane A (pipe %d) clock: %d\n",
- intel_crtc->pipe, crtc->mode.clock);
+ intel_crtc->pipe, crtc->mode.clock);
planea_clock = crtc->mode.clock;
} else {
DRM_DEBUG_KMS("plane B (pipe %d) clock: %d\n",
- intel_crtc->pipe, crtc->mode.clock);
+ intel_crtc->pipe, crtc->mode.clock);
planeb_clock = crtc->mode.clock;
}
sr_hdisplay = crtc->mode.hdisplay;
@@ -3502,62 +3572,35 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane;
- int fp_reg = (pipe == 0) ? FPA0 : FPB0;
- int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
- int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
- int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
- int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
- 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 = (plane == 0) ? DSPASIZE : DSPBSIZE;
- int dsppos_reg = (plane == 0) ? DSPAPOS : DSPBPOS;
- int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+ u32 fp_reg, dpll_reg;
int refclk, num_connectors = 0;
intel_clock_t clock, reduced_clock;
- u32 dpll = 0, fp = 0, fp2 = 0, dspcntr, pipeconf;
+ u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
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 intel_encoder *has_edp_encoder = NULL;
struct drm_mode_config *mode_config = &dev->mode_config;
- struct drm_encoder *encoder;
+ struct intel_encoder *encoder;
const intel_limit_t *limit;
int ret;
struct fdi_m_n m_n = {0};
- int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1;
- int data_n1_reg = (pipe == 0) ? PIPEA_DATA_N1 : PIPEB_DATA_N1;
- int link_m1_reg = (pipe == 0) ? PIPEA_LINK_M1 : PIPEB_LINK_M1;
- int link_n1_reg = (pipe == 0) ? PIPEA_LINK_N1 : PIPEB_LINK_N1;
- int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0;
- int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
- int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
- int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
- int trans_dpll_sel = (pipe == 0) ? 0 : 1;
- int lvds_reg = LVDS;
- u32 temp;
- int sdvo_pixel_multiply;
+ u32 reg, temp;
int target_clock;
drm_vblank_pre_modeset(dev, pipe);
- list_for_each_entry(encoder, &mode_config->encoder_list, head) {
- struct intel_encoder *intel_encoder;
-
- if (encoder->crtc != crtc)
+ list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+ if (encoder->base.crtc != crtc)
continue;
- intel_encoder = enc_to_intel_encoder(encoder);
- switch (intel_encoder->type) {
+ switch (encoder->type) {
case INTEL_OUTPUT_LVDS:
is_lvds = true;
break;
case INTEL_OUTPUT_SDVO:
case INTEL_OUTPUT_HDMI:
is_sdvo = true;
- if (intel_encoder->needs_tv_clock)
+ if (encoder->needs_tv_clock)
is_tv = true;
break;
case INTEL_OUTPUT_DVO:
@@ -3573,7 +3616,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
is_dp = true;
break;
case INTEL_OUTPUT_EDP:
- has_edp_encoder = intel_encoder;
+ has_edp_encoder = encoder;
break;
}
@@ -3583,15 +3626,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (is_lvds && dev_priv->lvds_use_ssc && 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_I9XX(dev)) {
+ refclk / 1000);
+ } else if (!IS_GEN2(dev)) {
refclk = 96000;
- if (HAS_PCH_SPLIT(dev))
+ if (HAS_PCH_SPLIT(dev) &&
+ (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)))
refclk = 120000; /* 120Mhz refclk */
} else {
refclk = 48000;
}
-
/*
* Returns a set of divisors for the desired target clock with the given
@@ -3607,13 +3650,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
/* Ensure that the cursor is valid for the new mode before changing... */
- intel_crtc_update_cursor(crtc);
+ intel_crtc_update_cursor(crtc, true);
if (is_lvds && dev_priv->lvds_downclock_avail) {
has_reduced_clock = limit->find_pll(limit, crtc,
- dev_priv->lvds_downclock,
- refclk,
- &reduced_clock);
+ dev_priv->lvds_downclock,
+ refclk,
+ &reduced_clock);
if (has_reduced_clock && (clock.p != reduced_clock.p)) {
/*
* If the different P is found, it means that we can't
@@ -3622,7 +3665,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
* feature.
*/
DRM_DEBUG_KMS("Different P is found for "
- "LVDS clock/downclock\n");
+ "LVDS clock/downclock\n");
has_reduced_clock = 0;
}
}
@@ -3630,14 +3673,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
this mirrors vbios setting. */
if (is_sdvo && is_tv) {
if (adjusted_mode->clock >= 100000
- && adjusted_mode->clock < 140500) {
+ && 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) {
+ && adjusted_mode->clock <= 200000) {
clock.p1 = 1;
clock.p2 = 10;
clock.n = 6;
@@ -3649,34 +3692,41 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* FDI link */
if (HAS_PCH_SPLIT(dev)) {
int lane = 0, link_bw, bpp;
- /* eDP doesn't require FDI link, so just set DP M/N
+ /* CPU eDP doesn't require FDI link, so just set DP M/N
according to current link config */
- if (has_edp_encoder) {
+ if (has_edp_encoder && !intel_encoder_is_pch_edp(&encoder->base)) {
target_clock = mode->clock;
intel_edp_link_config(has_edp_encoder,
&lane, &link_bw);
} else {
- /* DP over FDI requires target mode clock
+ /* [e]DP over FDI requires target mode clock
instead of link clock */
- if (is_dp)
+ if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
target_clock = mode->clock;
else
target_clock = adjusted_mode->clock;
- link_bw = 270000;
+
+ /* FDI is a binary signal running at ~2.7GHz, encoding
+ * each output octet as 10 bits. The actual frequency
+ * is stored as a divider into a 100MHz clock, and the
+ * mode pixel clock is stored in units of 1KHz.
+ * Hence the bw of each lane in terms of the mode signal
+ * is:
+ */
+ link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
}
/* determine panel color depth */
- temp = I915_READ(pipeconf_reg);
+ temp = I915_READ(PIPECONF(pipe));
temp &= ~PIPE_BPC_MASK;
if (is_lvds) {
- int lvds_reg = I915_READ(PCH_LVDS);
/* the BPC will be 6 if it is 18-bit LVDS panel */
- if ((lvds_reg & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
+ if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
temp |= PIPE_8BPC;
else
temp |= PIPE_6BPC;
- } else if (has_edp_encoder || (is_dp && intel_pch_has_edp(crtc))) {
- switch (dev_priv->edp_bpp/3) {
+ } else if (has_edp_encoder) {
+ switch (dev_priv->edp.bpp/3) {
case 8:
temp |= PIPE_8BPC;
break;
@@ -3692,8 +3742,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
} else
temp |= PIPE_8BPC;
- I915_WRITE(pipeconf_reg, temp);
- I915_READ(pipeconf_reg);
+ I915_WRITE(PIPECONF(pipe), temp);
switch (temp & PIPE_BPC_MASK) {
case PIPE_8BPC:
@@ -3738,33 +3787,39 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* Always enable nonspread source */
temp &= ~DREF_NONSPREAD_SOURCE_MASK;
temp |= DREF_NONSPREAD_SOURCE_ENABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
- POSTING_READ(PCH_DREF_CONTROL);
-
temp &= ~DREF_SSC_SOURCE_MASK;
temp |= DREF_SSC_SOURCE_ENABLE;
I915_WRITE(PCH_DREF_CONTROL, temp);
- POSTING_READ(PCH_DREF_CONTROL);
+ POSTING_READ(PCH_DREF_CONTROL);
udelay(200);
if (has_edp_encoder) {
if (dev_priv->lvds_use_ssc) {
temp |= DREF_SSC1_ENABLE;
I915_WRITE(PCH_DREF_CONTROL, temp);
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
-
- temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
- temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
- I915_WRITE(PCH_DREF_CONTROL, temp);
POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ }
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+
+ /* Enable CPU source on CPU attached eDP */
+ if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ if (dev_priv->lvds_use_ssc)
+ temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
+ else
+ temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
} else {
- temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
- I915_WRITE(PCH_DREF_CONTROL, temp);
- POSTING_READ(PCH_DREF_CONTROL);
+ /* Enable SSC on PCH eDP if needed */
+ if (dev_priv->lvds_use_ssc) {
+ DRM_ERROR("enabling SSC on PCH\n");
+ temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
+ }
}
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
}
}
@@ -3780,23 +3835,26 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
reduced_clock.m2;
}
+ dpll = 0;
if (!HAS_PCH_SPLIT(dev))
dpll = DPLL_VGA_MODE_DIS;
- if (IS_I9XX(dev)) {
+ if (!IS_GEN2(dev)) {
if (is_lvds)
dpll |= DPLLB_MODE_LVDS;
else
dpll |= DPLLB_MODE_DAC_SERIAL;
if (is_sdvo) {
+ int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+ if (pixel_multiplier > 1) {
+ if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+ dpll |= (pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+ else if (HAS_PCH_SPLIT(dev))
+ dpll |= (pixel_multiplier - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
+ }
dpll |= DPLL_DVO_HIGH_SPEED;
- sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
- if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
- dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
- else if (HAS_PCH_SPLIT(dev))
- dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
}
- if (is_dp)
+ if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
dpll |= DPLL_DVO_HIGH_SPEED;
/* compute bitmask from p1 value */
@@ -3824,7 +3882,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
break;
}
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev))
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
} else {
if (is_lvds) {
@@ -3851,7 +3909,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll |= PLL_REF_INPUT_DREFCLK;
/* setup pipeconf */
- pipeconf = I915_READ(pipeconf_reg);
+ pipeconf = I915_READ(PIPECONF(pipe));
/* Set up the display plane register */
dspcntr = DISPPLANE_GAMMA_ENABLE;
@@ -3865,7 +3923,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dspcntr |= DISPPLANE_SEL_PIPE_B;
}
- if (pipe == 0 && !IS_I965G(dev)) {
+ if (pipe == 0 && INTEL_INFO(dev)->gen < 4) {
/* Enable pixel doubling when the dot clock is > 90% of the (display)
* core speed.
*
@@ -3874,51 +3932,47 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
*/
if (mode->clock >
dev_priv->display.get_display_clock_speed(dev) * 9 / 10)
- pipeconf |= PIPEACONF_DOUBLE_WIDE;
+ pipeconf |= PIPECONF_DOUBLE_WIDE;
else
- pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
+ pipeconf &= ~PIPECONF_DOUBLE_WIDE;
}
dspcntr |= DISPLAY_PLANE_ENABLE;
- pipeconf |= PIPEACONF_ENABLE;
+ pipeconf |= PIPECONF_ENABLE;
dpll |= DPLL_VCO_ENABLE;
-
- /* Disable the panel fitter if it was on our pipe */
- if (!HAS_PCH_SPLIT(dev) && intel_panel_fitter_pipe(dev) == pipe)
- I915_WRITE(PFIT_CONTROL, 0);
-
DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
drm_mode_debug_printmodeline(mode);
/* assign to Ironlake registers */
if (HAS_PCH_SPLIT(dev)) {
- fp_reg = pch_fp_reg;
- dpll_reg = pch_dpll_reg;
+ fp_reg = PCH_FP0(pipe);
+ dpll_reg = PCH_DPLL(pipe);
+ } else {
+ fp_reg = FP0(pipe);
+ dpll_reg = DPLL(pipe);
}
- if (!has_edp_encoder) {
+ /* PCH eDP needs FDI, but CPU eDP does not */
+ if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
I915_WRITE(fp_reg, fp);
I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
- I915_READ(dpll_reg);
+
+ POSTING_READ(dpll_reg);
udelay(150);
}
/* enable transcoder DPLL */
if (HAS_PCH_CPT(dev)) {
temp = I915_READ(PCH_DPLL_SEL);
- if (trans_dpll_sel == 0)
- temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
+ if (pipe == 0)
+ temp |= TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL;
else
- temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
+ temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL;
I915_WRITE(PCH_DPLL_SEL, temp);
- I915_READ(PCH_DPLL_SEL);
- udelay(150);
- }
- if (HAS_PCH_SPLIT(dev)) {
- pipeconf &= ~PIPE_ENABLE_DITHER;
- pipeconf &= ~PIPE_DITHER_TYPE_MASK;
+ POSTING_READ(PCH_DPLL_SEL);
+ udelay(150);
}
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
@@ -3926,58 +3980,60 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
* things on.
*/
if (is_lvds) {
- u32 lvds;
-
+ reg = LVDS;
if (HAS_PCH_SPLIT(dev))
- lvds_reg = PCH_LVDS;
+ reg = PCH_LVDS;
- lvds = I915_READ(lvds_reg);
- lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
+ temp = I915_READ(reg);
+ temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
if (pipe == 1) {
if (HAS_PCH_CPT(dev))
- lvds |= PORT_TRANS_B_SEL_CPT;
+ temp |= PORT_TRANS_B_SEL_CPT;
else
- lvds |= LVDS_PIPEB_SELECT;
+ temp |= LVDS_PIPEB_SELECT;
} else {
if (HAS_PCH_CPT(dev))
- lvds &= ~PORT_TRANS_SEL_MASK;
+ temp &= ~PORT_TRANS_SEL_MASK;
else
- lvds &= ~LVDS_PIPEB_SELECT;
+ temp &= ~LVDS_PIPEB_SELECT;
}
/* set the corresponsding LVDS_BORDER bit */
- lvds |= dev_priv->lvds_border_bits;
+ temp |= dev_priv->lvds_border_bits;
/* Set the B0-B3 data pairs corresponding to whether we're going to
* set the DPLLs for dual-channel mode or not.
*/
if (clock.p2 == 7)
- lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+ temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
else
- lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+ temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
* appropriately here, but we need to look more thoroughly into how
* panels behave in the two modes.
*/
- /* set the dithering flag */
- if (IS_I965G(dev)) {
- if (dev_priv->lvds_dither) {
- if (HAS_PCH_SPLIT(dev)) {
- pipeconf |= PIPE_ENABLE_DITHER;
- pipeconf |= PIPE_DITHER_TYPE_ST01;
- } else
- lvds |= LVDS_ENABLE_DITHER;
- } else {
- if (!HAS_PCH_SPLIT(dev)) {
- lvds &= ~LVDS_ENABLE_DITHER;
- }
- }
+ /* set the dithering flag on non-PCH LVDS as needed */
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
+ if (dev_priv->lvds_dither)
+ temp |= LVDS_ENABLE_DITHER;
+ else
+ temp &= ~LVDS_ENABLE_DITHER;
}
- I915_WRITE(lvds_reg, lvds);
- I915_READ(lvds_reg);
+ I915_WRITE(reg, temp);
}
- if (is_dp)
+
+ /* set the dithering flag and clear for anything other than a panel. */
+ if (HAS_PCH_SPLIT(dev)) {
+ pipeconf &= ~PIPECONF_DITHER_EN;
+ pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
+ if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+ pipeconf |= PIPECONF_DITHER_EN;
+ pipeconf |= PIPECONF_DITHER_TYPE_ST1;
+ }
+ }
+
+ if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
intel_dp_set_m_n(crtc, mode, adjusted_mode);
- else if (HAS_PCH_SPLIT(dev)) {
+ } else if (HAS_PCH_SPLIT(dev)) {
/* For non-DP output, clear any trans DP clock recovery setting.*/
if (pipe == 0) {
I915_WRITE(TRANSA_DATA_M1, 0);
@@ -3992,29 +4048,35 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
}
- if (!has_edp_encoder) {
+ if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
I915_WRITE(fp_reg, fp);
I915_WRITE(dpll_reg, dpll);
- I915_READ(dpll_reg);
+
/* Wait for the clocks to stabilize. */
+ POSTING_READ(dpll_reg);
udelay(150);
- if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
+ temp = 0;
if (is_sdvo) {
- sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
- I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
- ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
- } else
- I915_WRITE(dpll_md_reg, 0);
+ temp = intel_mode_get_pixel_multiplier(adjusted_mode);
+ if (temp > 1)
+ temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+ else
+ temp = 0;
+ }
+ I915_WRITE(DPLL_MD(pipe), temp);
} else {
/* write it again -- the BIOS does, after all */
I915_WRITE(dpll_reg, dpll);
}
- I915_READ(dpll_reg);
+
/* Wait for the clocks to stabilize. */
+ POSTING_READ(dpll_reg);
udelay(150);
}
+ intel_crtc->lowfreq_avail = false;
if (is_lvds && has_reduced_clock && i915_powersave) {
I915_WRITE(fp_reg + 4, fp2);
intel_crtc->lowfreq_avail = true;
@@ -4024,7 +4086,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
} else {
I915_WRITE(fp_reg + 4, fp);
- intel_crtc->lowfreq_avail = false;
if (HAS_PIPE_CXSR(dev)) {
DRM_DEBUG_KMS("disabling CxSR downclocking\n");
pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
@@ -4043,70 +4104,62 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
} else
pipeconf &= ~PIPECONF_INTERLACE_W_FIELD_INDICATION; /* progressive */
- I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+ I915_WRITE(HTOTAL(pipe),
+ (adjusted_mode->crtc_hdisplay - 1) |
((adjusted_mode->crtc_htotal - 1) << 16));
- I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+ I915_WRITE(HBLANK(pipe),
+ (adjusted_mode->crtc_hblank_start - 1) |
((adjusted_mode->crtc_hblank_end - 1) << 16));
- I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+ I915_WRITE(HSYNC(pipe),
+ (adjusted_mode->crtc_hsync_start - 1) |
((adjusted_mode->crtc_hsync_end - 1) << 16));
- I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+
+ I915_WRITE(VTOTAL(pipe),
+ (adjusted_mode->crtc_vdisplay - 1) |
((adjusted_mode->crtc_vtotal - 1) << 16));
- I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+ I915_WRITE(VBLANK(pipe),
+ (adjusted_mode->crtc_vblank_start - 1) |
((adjusted_mode->crtc_vblank_end - 1) << 16));
- I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+ I915_WRITE(VSYNC(pipe),
+ (adjusted_mode->crtc_vsync_start - 1) |
((adjusted_mode->crtc_vsync_end - 1) << 16));
- /* pipesrc and dspsize control the size that is scaled from, which should
- * always be the user's requested size.
+
+ /* pipesrc and dspsize control the size that is scaled from,
+ * which should always be the user's requested size.
*/
if (!HAS_PCH_SPLIT(dev)) {
- I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
- (mode->hdisplay - 1));
- I915_WRITE(dsppos_reg, 0);
+ I915_WRITE(DSPSIZE(plane),
+ ((mode->vdisplay - 1) << 16) |
+ (mode->hdisplay - 1));
+ I915_WRITE(DSPPOS(plane), 0);
}
- I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+ I915_WRITE(PIPESRC(pipe),
+ ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
if (HAS_PCH_SPLIT(dev)) {
- I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
- I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
- I915_WRITE(link_m1_reg, m_n.link_m);
- I915_WRITE(link_n1_reg, m_n.link_n);
+ I915_WRITE(PIPE_DATA_M1(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m);
+ I915_WRITE(PIPE_DATA_N1(pipe), m_n.gmch_n);
+ I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m);
+ I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n);
- if (has_edp_encoder) {
+ if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
ironlake_set_pll_edp(crtc, adjusted_mode->clock);
- } else {
- /* enable FDI RX PLL too */
- temp = I915_READ(fdi_rx_reg);
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
- I915_READ(fdi_rx_reg);
- udelay(200);
-
- /* enable FDI TX PLL too */
- temp = I915_READ(fdi_tx_reg);
- I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
- I915_READ(fdi_tx_reg);
-
- /* enable FDI RX PCDCLK */
- temp = I915_READ(fdi_rx_reg);
- I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK);
- I915_READ(fdi_rx_reg);
- udelay(200);
}
}
- I915_WRITE(pipeconf_reg, pipeconf);
- I915_READ(pipeconf_reg);
+ I915_WRITE(PIPECONF(pipe), pipeconf);
+ POSTING_READ(PIPECONF(pipe));
intel_wait_for_vblank(dev, pipe);
- if (IS_IRONLAKE(dev)) {
+ 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_reg, dspcntr);
+ I915_WRITE(DSPCNTR(plane), dspcntr);
- /* Flush the plane changes */
ret = intel_pipe_set_base(crtc, x, y, old_fb);
intel_update_watermarks(dev);
@@ -4199,7 +4252,8 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
}
/* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
-static void intel_crtc_update_cursor(struct drm_crtc *crtc)
+static void intel_crtc_update_cursor(struct drm_crtc *crtc,
+ bool on)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4212,7 +4266,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc)
pos = 0;
- if (intel_crtc->cursor_on && crtc->fb) {
+ if (on && crtc->enabled && crtc->fb) {
base = intel_crtc->cursor_addr;
if (x > (int) crtc->fb->width)
base = 0;
@@ -4324,7 +4378,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
addr = obj_priv->phys_obj->handle->busaddr;
}
- if (!IS_I9XX(dev))
+ if (IS_GEN2(dev))
I915_WRITE(CURSIZE, (height << 12) | width);
finish:
@@ -4344,7 +4398,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
intel_crtc->cursor_width = width;
intel_crtc->cursor_height = height;
- intel_crtc_update_cursor(crtc);
+ intel_crtc_update_cursor(crtc, true);
return 0;
fail_unpin:
@@ -4363,7 +4417,7 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
intel_crtc->cursor_x = x;
intel_crtc->cursor_y = y;
- intel_crtc_update_cursor(crtc);
+ intel_crtc_update_cursor(crtc, true);
return 0;
}
@@ -4432,7 +4486,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
struct intel_crtc *intel_crtc;
struct drm_crtc *possible_crtc;
struct drm_crtc *supported_crtc =NULL;
- struct drm_encoder *encoder = &intel_encoder->enc;
+ struct drm_encoder *encoder = &intel_encoder->base;
struct drm_crtc *crtc = NULL;
struct drm_device *dev = encoder->dev;
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
@@ -4513,7 +4567,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector, int dpms_mode)
{
- struct drm_encoder *encoder = &intel_encoder->enc;
+ struct drm_encoder *encoder = &intel_encoder->base;
struct drm_device *dev = encoder->dev;
struct drm_crtc *crtc = encoder->crtc;
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
@@ -4559,7 +4613,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
}
- if (IS_I9XX(dev)) {
+ if (!IS_GEN2(dev)) {
if (IS_PINEVIEW(dev))
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >>
DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW);
@@ -4663,8 +4717,6 @@ static void intel_gpu_idle_timer(unsigned long arg)
struct drm_device *dev = (struct drm_device *)arg;
drm_i915_private_t *dev_priv = dev->dev_private;
- DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
-
dev_priv->busy = false;
queue_work(dev_priv->wq, &dev_priv->idle_work);
@@ -4678,14 +4730,12 @@ static void intel_crtc_idle_timer(unsigned long arg)
struct drm_crtc *crtc = &intel_crtc->base;
drm_i915_private_t *dev_priv = crtc->dev->dev_private;
- DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
-
intel_crtc->busy = false;
queue_work(dev_priv->wq, &dev_priv->idle_work);
}
-static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
+static void intel_increase_pllclock(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -4720,9 +4770,8 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
}
/* Schedule downclock */
- if (schedule)
- mod_timer(&intel_crtc->idle_timer, jiffies +
- msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
+ mod_timer(&intel_crtc->idle_timer, jiffies +
+ msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
}
static void intel_decrease_pllclock(struct drm_crtc *crtc)
@@ -4858,7 +4907,7 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK);
}
/* Non-busy -> busy, upclock */
- intel_increase_pllclock(crtc, true);
+ intel_increase_pllclock(crtc);
intel_crtc->busy = true;
} else {
/* Busy -> busy, put off timer */
@@ -4872,8 +4921,22 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
static void intel_crtc_destroy(struct drm_crtc *crtc)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct intel_unpin_work *work;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ work = intel_crtc->unpin_work;
+ intel_crtc->unpin_work = NULL;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ if (work) {
+ cancel_work_sync(&work->work);
+ kfree(work);
+ }
drm_crtc_cleanup(crtc);
+
kfree(intel_crtc);
}
@@ -4928,12 +4991,11 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
spin_unlock_irqrestore(&dev->event_lock, flags);
- obj_priv = to_intel_bo(work->pending_flip_obj);
-
- /* Initial scanout buffer will have a 0 pending flip count */
- if ((atomic_read(&obj_priv->pending_flip) == 0) ||
- atomic_dec_and_test(&obj_priv->pending_flip))
- DRM_WAKEUP(&dev_priv->pending_flip_queue);
+ obj_priv = to_intel_bo(work->old_fb_obj);
+ atomic_clear_mask(1 << intel_crtc->plane,
+ &obj_priv->pending_flip.counter);
+ if (atomic_read(&obj_priv->pending_flip) == 0)
+ wake_up(&dev_priv->pending_flip_queue);
schedule_work(&work->work);
trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj);
@@ -5014,7 +5076,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
obj = intel_fb->obj;
mutex_lock(&dev->struct_mutex);
- ret = intel_pin_and_fence_fb_obj(dev, obj);
+ ret = intel_pin_and_fence_fb_obj(dev, obj, true);
if (ret)
goto cleanup_work;
@@ -5023,29 +5085,33 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
drm_gem_object_reference(obj);
crtc->fb = fb;
- ret = i915_gem_object_flush_write_domain(obj);
- if (ret)
- goto cleanup_objs;
ret = drm_vblank_get(dev, intel_crtc->pipe);
if (ret)
goto cleanup_objs;
- obj_priv = to_intel_bo(obj);
- atomic_inc(&obj_priv->pending_flip);
+ /* Block clients from rendering to the new back buffer until
+ * the flip occurs and the object is no longer visible.
+ */
+ atomic_add(1 << intel_crtc->plane,
+ &to_intel_bo(work->old_fb_obj)->pending_flip);
+
work->pending_flip_obj = obj;
+ obj_priv = to_intel_bo(obj);
if (IS_GEN3(dev) || IS_GEN2(dev)) {
u32 flip_mask;
+ /* Can't queue multiple flips, so wait for the previous
+ * one to finish before executing the next.
+ */
+ BEGIN_LP_RING(2);
if (intel_crtc->plane)
flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
else
flip_mask = MI_WAIT_FOR_PLANE_A_FLIP;
-
- BEGIN_LP_RING(2);
OUT_RING(MI_WAIT_FOR_EVENT | flip_mask);
- OUT_RING(0);
+ OUT_RING(MI_NOOP);
ADVANCE_LP_RING();
}
@@ -5126,15 +5192,14 @@ cleanup_work:
return ret;
}
-static const struct drm_crtc_helper_funcs intel_helper_funcs = {
+static struct drm_crtc_helper_funcs intel_helper_funcs = {
.dpms = intel_crtc_dpms,
.mode_fixup = intel_crtc_mode_fixup,
.mode_set = intel_crtc_mode_set,
.mode_set_base = intel_pipe_set_base,
.mode_set_base_atomic = intel_pipe_set_base_atomic,
- .prepare = intel_crtc_prepare,
- .commit = intel_crtc_commit,
.load_lut = intel_crtc_load_lut,
+ .disable = intel_crtc_disable,
};
static const struct drm_crtc_funcs intel_crtc_funcs = {
@@ -5160,8 +5225,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
- intel_crtc->pipe = pipe;
- intel_crtc->plane = pipe;
for (i = 0; i < 256; i++) {
intel_crtc->lut_r[i] = i;
intel_crtc->lut_g[i] = i;
@@ -5171,9 +5234,9 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
/* Swap pipes & planes for FBC on pre-965 */
intel_crtc->pipe = pipe;
intel_crtc->plane = pipe;
- if (IS_MOBILE(dev) && (IS_I9XX(dev) && !IS_I965G(dev))) {
+ if (IS_MOBILE(dev) && IS_GEN3(dev)) {
DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
- intel_crtc->plane = ((pipe == 0) ? 1 : 0);
+ intel_crtc->plane = !pipe;
}
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
@@ -5183,6 +5246,16 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
intel_crtc->cursor_addr = 0;
intel_crtc->dpms_mode = -1;
+ intel_crtc->active = true; /* force the pipe off on setup_init_config */
+
+ if (HAS_PCH_SPLIT(dev)) {
+ intel_helper_funcs.prepare = ironlake_crtc_prepare;
+ intel_helper_funcs.commit = ironlake_crtc_commit;
+ } else {
+ intel_helper_funcs.prepare = i9xx_crtc_prepare;
+ intel_helper_funcs.commit = i9xx_crtc_commit;
+ }
+
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
intel_crtc->busy = false;
@@ -5218,38 +5291,25 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
return 0;
}
-struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
-{
- struct drm_crtc *crtc = NULL;
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- if (intel_crtc->pipe == pipe)
- break;
- }
- return crtc;
-}
-
static int intel_encoder_clones(struct drm_device *dev, int type_mask)
{
+ struct intel_encoder *encoder;
int index_mask = 0;
- struct drm_encoder *encoder;
int entry = 0;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
- if (type_mask & intel_encoder->clone_mask)
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ if (type_mask & encoder->clone_mask)
index_mask |= (1 << entry);
entry++;
}
+
return index_mask;
}
-
static void intel_setup_outputs(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_encoder *encoder;
+ struct intel_encoder *encoder;
bool dpd_is_edp = false;
if (IS_MOBILE(dev) && !IS_I830(dev))
@@ -5338,12 +5398,10 @@ static void intel_setup_outputs(struct drm_device *dev)
if (SUPPORTS_TV(dev))
intel_tv_init(dev);
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
-
- encoder->possible_crtcs = intel_encoder->crtc_mask;
- encoder->possible_clones = intel_encoder_clones(dev,
- intel_encoder->clone_mask);
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ encoder->base.possible_crtcs = encoder->crtc_mask;
+ encoder->base.possible_clones =
+ intel_encoder_clones(dev, encoder->clone_mask);
}
}
@@ -5377,8 +5435,25 @@ int intel_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd *mode_cmd,
struct drm_gem_object *obj)
{
+ struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
int ret;
+ if (obj_priv->tiling_mode == I915_TILING_Y)
+ return -EINVAL;
+
+ if (mode_cmd->pitch & 63)
+ return -EINVAL;
+
+ switch (mode_cmd->bpp) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ return -EINVAL;
+ }
+
ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
if (ret) {
DRM_ERROR("framebuffer init failed %d\n", ret);
@@ -5487,6 +5562,10 @@ void ironlake_enable_drps(struct drm_device *dev)
u32 rgvmodectl = I915_READ(MEMMODECTL);
u8 fmax, fmin, fstart, vstart;
+ /* Enable temp reporting */
+ I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN);
+ I915_WRITE16(TSC1, I915_READ(TSC1) | TSE);
+
/* 100ms RC evaluation intervals */
I915_WRITE(RCUPEI, 100000);
I915_WRITE(RCDNEI, 100000);
@@ -5529,7 +5608,7 @@ void ironlake_enable_drps(struct drm_device *dev)
rgvmodectl |= MEMMODE_SWMODE_EN;
I915_WRITE(MEMMODECTL, rgvmodectl);
- if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 1, 0))
+ if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
DRM_ERROR("stuck trying to change perf mode\n");
msleep(1);
@@ -5660,7 +5739,7 @@ void intel_init_clock_gating(struct drm_device *dev)
if (HAS_PCH_SPLIT(dev)) {
uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
- if (IS_IRONLAKE(dev)) {
+ if (IS_GEN5(dev)) {
/* Required for FBC */
dspclk_gate |= DPFDUNIT_CLOCK_GATE_DISABLE;
/* Required for CxSR */
@@ -5674,13 +5753,20 @@ void intel_init_clock_gating(struct drm_device *dev)
I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
/*
+ * On Ibex Peak and Cougar Point, we need to disable clock
+ * gating for the panel power sequencer or it will fail to
+ * start up when no ports are active.
+ */
+ I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
+
+ /*
* According to the spec the following bits should be set in
* order to enable memory self-refresh
* The bit 22/21 of 0x42004
* The bit 5 of 0x42020
* The bit 15 of 0x45000
*/
- if (IS_IRONLAKE(dev)) {
+ if (IS_GEN5(dev)) {
I915_WRITE(ILK_DISPLAY_CHICKEN2,
(I915_READ(ILK_DISPLAY_CHICKEN2) |
ILK_DPARB_GATE | ILK_VSDPFD_FULL));
@@ -5728,20 +5814,20 @@ void intel_init_clock_gating(struct drm_device *dev)
if (IS_GM45(dev))
dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE;
I915_WRITE(DSPCLK_GATE_D, dspclk_gate);
- } else if (IS_I965GM(dev)) {
+ } else if (IS_CRESTLINE(dev)) {
I915_WRITE(RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE);
I915_WRITE(RENCLK_GATE_D2, 0);
I915_WRITE(DSPCLK_GATE_D, 0);
I915_WRITE(RAMCLK_GATE_D, 0);
I915_WRITE16(DEUC, 0);
- } else if (IS_I965G(dev)) {
+ } else if (IS_BROADWATER(dev)) {
I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE |
I965_RCC_CLOCK_GATE_DISABLE |
I965_RCPB_CLOCK_GATE_DISABLE |
I965_ISC_CLOCK_GATE_DISABLE |
I965_FBC_CLOCK_GATE_DISABLE);
I915_WRITE(RENCLK_GATE_D2, 0);
- } else if (IS_I9XX(dev)) {
+ } else if (IS_GEN3(dev)) {
u32 dstate = I915_READ(D_STATE);
dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
@@ -5823,7 +5909,7 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.fbc_enabled = g4x_fbc_enabled;
dev_priv->display.enable_fbc = g4x_enable_fbc;
dev_priv->display.disable_fbc = g4x_disable_fbc;
- } else if (IS_I965GM(dev)) {
+ } else if (IS_CRESTLINE(dev)) {
dev_priv->display.fbc_enabled = i8xx_fbc_enabled;
dev_priv->display.enable_fbc = i8xx_enable_fbc;
dev_priv->display.disable_fbc = i8xx_disable_fbc;
@@ -5856,7 +5942,7 @@ static void intel_init_display(struct drm_device *dev)
/* For FIFO watermark updates */
if (HAS_PCH_SPLIT(dev)) {
- if (IS_IRONLAKE(dev)) {
+ if (IS_GEN5(dev)) {
if (I915_READ(MLTR_ILK) & ILK_SRLT_MASK)
dev_priv->display.update_wm = ironlake_update_wm;
else {
@@ -5883,9 +5969,9 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.update_wm = pineview_update_wm;
} else if (IS_G4X(dev))
dev_priv->display.update_wm = g4x_update_wm;
- else if (IS_I965G(dev))
+ else if (IS_GEN4(dev))
dev_priv->display.update_wm = i965_update_wm;
- else if (IS_I9XX(dev)) {
+ else if (IS_GEN3(dev)) {
dev_priv->display.update_wm = i9xx_update_wm;
dev_priv->display.get_fifo_size = i9xx_get_fifo_size;
} else if (IS_I85X(dev)) {
@@ -5999,24 +6085,24 @@ void intel_modeset_init(struct drm_device *dev)
intel_init_display(dev);
- if (IS_I965G(dev)) {
- dev->mode_config.max_width = 8192;
- dev->mode_config.max_height = 8192;
- } else if (IS_I9XX(dev)) {
+ if (IS_GEN2(dev)) {
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+ } else if (IS_GEN3(dev)) {
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
} else {
- dev->mode_config.max_width = 2048;
- dev->mode_config.max_height = 2048;
+ dev->mode_config.max_width = 8192;
+ dev->mode_config.max_height = 8192;
}
/* set memory base */
- if (IS_I9XX(dev))
- dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2);
- else
+ if (IS_GEN2(dev))
dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
+ else
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2);
- if (IS_MOBILE(dev) || IS_I9XX(dev))
+ if (IS_MOBILE(dev) || !IS_GEN2(dev))
dev_priv->num_pipe = 2;
else
dev_priv->num_pipe = 1;
@@ -6052,10 +6138,11 @@ void intel_modeset_cleanup(struct drm_device *dev)
struct drm_crtc *crtc;
struct intel_crtc *intel_crtc;
+ drm_kms_helper_poll_fini(dev);
mutex_lock(&dev->struct_mutex);
- drm_kms_helper_poll_fini(dev);
- intel_fbdev_fini(dev);
+ intel_unregister_dsm_handler();
+
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
/* Skip inactive CRTCs */
@@ -6063,12 +6150,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
continue;
intel_crtc = to_intel_crtc(crtc);
- intel_increase_pllclock(crtc, false);
- del_timer_sync(&intel_crtc->idle_timer);
+ intel_increase_pllclock(crtc);
}
- del_timer_sync(&dev_priv->idle_timer);
-
if (dev_priv->display.disable_fbc)
dev_priv->display.disable_fbc(dev);
@@ -6097,33 +6181,36 @@ void intel_modeset_cleanup(struct drm_device *dev)
mutex_unlock(&dev->struct_mutex);
+ /* Disable the irq before mode object teardown, for the irq might
+ * enqueue unpin/hotplug work. */
+ drm_irq_uninstall(dev);
+ cancel_work_sync(&dev_priv->hotplug_work);
+
+ /* Shut off idle work before the crtcs get freed. */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ intel_crtc = to_intel_crtc(crtc);
+ del_timer_sync(&intel_crtc->idle_timer);
+ }
+ del_timer_sync(&dev_priv->idle_timer);
+ cancel_work_sync(&dev_priv->idle_work);
+
drm_mode_config_cleanup(dev);
}
-
/*
* Return which encoder is currently attached for connector.
*/
-struct drm_encoder *intel_attached_encoder (struct drm_connector *connector)
+struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
{
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
- int i;
-
- for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
- if (connector->encoder_ids[i] == 0)
- break;
-
- obj = drm_mode_object_find(connector->dev,
- connector->encoder_ids[i],
- DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- continue;
+ return &intel_attached_encoder(connector)->base;
+}
- encoder = obj_to_encoder(obj);
- return encoder;
- }
- return NULL;
+void intel_connector_attach_encoder(struct intel_connector *connector,
+ struct intel_encoder *encoder)
+{
+ connector->encoder = encoder;
+ drm_mode_connector_attach_encoder(&connector->base,
+ &encoder->base);
}
/*
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 9ab8708ac6ba..891f4f1d63b1 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -42,15 +42,13 @@
#define DP_LINK_CONFIGURATION_SIZE 9
-#define IS_eDP(i) ((i)->base.type == INTEL_OUTPUT_EDP)
-#define IS_PCH_eDP(i) ((i)->is_pch_edp)
-
struct intel_dp {
struct intel_encoder base;
uint32_t output_reg;
uint32_t DP;
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
bool has_audio;
+ int force_audio;
int dpms_mode;
uint8_t link_bw;
uint8_t lane_count;
@@ -58,14 +56,69 @@ struct intel_dp {
struct i2c_adapter adapter;
struct i2c_algo_dp_aux_data algo;
bool is_pch_edp;
+ uint8_t train_set[4];
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+ struct drm_property *force_audio_property;
};
+/**
+ * is_edp - is the given port attached to an eDP panel (either CPU or PCH)
+ * @intel_dp: DP struct
+ *
+ * If a CPU or PCH DP output is attached to an eDP panel, this function
+ * will return true, and false otherwise.
+ */
+static bool is_edp(struct intel_dp *intel_dp)
+{
+ return intel_dp->base.type == INTEL_OUTPUT_EDP;
+}
+
+/**
+ * is_pch_edp - is the port on the PCH and attached to an eDP panel?
+ * @intel_dp: DP struct
+ *
+ * Returns true if the given DP struct corresponds to a PCH DP port attached
+ * to an eDP panel, false otherwise. Helpful for determining whether we
+ * may need FDI resources for a given DP output or not.
+ */
+static bool is_pch_edp(struct intel_dp *intel_dp)
+{
+ return intel_dp->is_pch_edp;
+}
+
static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
{
- return container_of(enc_to_intel_encoder(encoder), struct intel_dp, base);
+ return container_of(encoder, struct intel_dp, base.base);
+}
+
+static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
+{
+ return container_of(intel_attached_encoder(connector),
+ struct intel_dp, base);
+}
+
+/**
+ * intel_encoder_is_pch_edp - is the given encoder a PCH attached eDP?
+ * @encoder: DRM encoder
+ *
+ * Return true if @encoder corresponds to a PCH attached eDP panel. Needed
+ * by intel_display.c.
+ */
+bool intel_encoder_is_pch_edp(struct drm_encoder *encoder)
+{
+ struct intel_dp *intel_dp;
+
+ if (!encoder)
+ return false;
+
+ intel_dp = enc_to_intel_dp(encoder);
+
+ return is_pch_edp(intel_dp);
}
-static void intel_dp_link_train(struct intel_dp *intel_dp);
+static void intel_dp_start_link_train(struct intel_dp *intel_dp);
+static void intel_dp_complete_link_train(struct intel_dp *intel_dp);
static void intel_dp_link_down(struct intel_dp *intel_dp);
void
@@ -129,8 +182,8 @@ intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pi
{
struct drm_i915_private *dev_priv = dev->dev_private;
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
- return (pixel_clock * dev_priv->edp_bpp) / 8;
+ if (is_edp(intel_dp))
+ return (pixel_clock * dev_priv->edp.bpp + 7) / 8;
else
return pixel_clock * 3;
}
@@ -145,15 +198,13 @@ static int
intel_dp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp));
int max_lanes = intel_dp_max_lane_count(intel_dp);
- if ((IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) &&
- dev_priv->panel_fixed_mode) {
+ if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay)
return MODE_PANEL;
@@ -163,7 +214,7 @@ intel_dp_mode_valid(struct drm_connector *connector,
/* only refuse the mode on non eDP since we have seen some wierd eDP panels
which are outside spec tolerances but somehow work by magic */
- if (!IS_eDP(intel_dp) &&
+ if (!is_edp(intel_dp) &&
(intel_dp_link_required(connector->dev, intel_dp, mode->clock)
> intel_dp_max_data_rate(max_link_clock, max_lanes)))
return MODE_CLOCK_HIGH;
@@ -233,7 +284,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
uint8_t *recv, int recv_size)
{
uint32_t output_reg = intel_dp->output_reg;
- struct drm_device *dev = intel_dp->base.enc.dev;
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t ch_ctl = output_reg + 0x10;
uint32_t ch_data = ch_ctl + 4;
@@ -246,8 +297,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
/* The clock divider is based off the hrawclk,
* and would like to run at 2MHz. So, take the
* hrawclk value and divide by 2 and use that
+ *
+ * Note that PCH attached eDP panels should use a 125MHz input
+ * clock divider.
*/
- if (IS_eDP(intel_dp)) {
+ if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) {
if (IS_GEN6(dev))
aux_clock_divider = 200; /* SNB eDP input clock at 400Mhz */
else
@@ -519,8 +573,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0;
static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
- if ((IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) &&
- dev_priv->panel_fixed_mode) {
+ if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode);
intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN,
mode, adjusted_mode);
@@ -531,6 +584,17 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
mode->clock = dev_priv->panel_fixed_mode->clock;
}
+ /* Just use VBT values for eDP */
+ if (is_edp(intel_dp)) {
+ intel_dp->lane_count = dev_priv->edp.lanes;
+ intel_dp->link_bw = dev_priv->edp.rate;
+ adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw);
+ DRM_DEBUG_KMS("eDP link bw %02x lane count %d clock %d\n",
+ intel_dp->link_bw, intel_dp->lane_count,
+ adjusted_mode->clock);
+ return true;
+ }
+
for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
for (clock = 0; clock <= max_clock; clock++) {
int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count);
@@ -549,19 +613,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
}
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) {
- /* okay we failed just pick the highest */
- intel_dp->lane_count = max_lane_count;
- intel_dp->link_bw = bws[max_clock];
- adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw);
- DRM_DEBUG_KMS("Force picking display port link bw %02x lane "
- "count %d clock %d\n",
- intel_dp->link_bw, intel_dp->lane_count,
- adjusted_mode->clock);
-
- return true;
- }
-
return false;
}
@@ -598,25 +649,6 @@ intel_dp_compute_m_n(int bpp,
intel_reduce_ratio(&m_n->link_m, &m_n->link_n);
}
-bool intel_pch_has_edp(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_mode_config *mode_config = &dev->mode_config;
- struct drm_encoder *encoder;
-
- list_for_each_entry(encoder, &mode_config->encoder_list, head) {
- struct intel_dp *intel_dp;
-
- if (encoder->crtc != crtc)
- continue;
-
- intel_dp = enc_to_intel_dp(encoder);
- if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT)
- return intel_dp->is_pch_edp;
- }
- return false;
-}
-
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -641,8 +673,10 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
intel_dp = enc_to_intel_dp(encoder);
if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT) {
lane_count = intel_dp->lane_count;
- if (IS_PCH_eDP(intel_dp))
- bpp = dev_priv->edp_bpp;
+ break;
+ } else if (is_edp(intel_dp)) {
+ lane_count = dev_priv->edp.lanes;
+ bpp = dev_priv->edp.bpp;
break;
}
}
@@ -698,7 +732,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
{
struct drm_device *dev = encoder->dev;
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- struct drm_crtc *crtc = intel_dp->base.enc.crtc;
+ struct drm_crtc *crtc = intel_dp->base.base.crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
intel_dp->DP = (DP_VOLTAGE_0_4 |
@@ -709,7 +743,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
intel_dp->DP |= DP_SYNC_VS_HIGH;
- if (HAS_PCH_CPT(dev) && !IS_eDP(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
else
intel_dp->DP |= DP_LINK_TRAIN_OFF;
@@ -744,7 +778,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (intel_crtc->pipe == 1 && !HAS_PCH_CPT(dev))
intel_dp->DP |= DP_PIPEB_SELECT;
- if (IS_eDP(intel_dp)) {
+ if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) {
/* don't miss out required setting for eDP */
intel_dp->DP |= DP_PLL_ENABLE;
if (adjusted_mode->clock < 200000)
@@ -754,13 +788,15 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
}
-static void ironlake_edp_panel_on (struct drm_device *dev)
+/* Returns true if the panel was already on when called */
+static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
{
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 pp;
+ u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE;
if (I915_READ(PCH_PP_STATUS) & PP_ON)
- return;
+ return true;
pp = I915_READ(PCH_PP_CONTROL);
@@ -771,21 +807,30 @@ static void ironlake_edp_panel_on (struct drm_device *dev)
pp |= PANEL_UNLOCK_REGS | POWER_TARGET_ON;
I915_WRITE(PCH_PP_CONTROL, pp);
+ POSTING_READ(PCH_PP_CONTROL);
+
+ /* Ouch. We need to wait here for some panels, like Dell e6510
+ * https://bugs.freedesktop.org/show_bug.cgi?id=29278i
+ */
+ msleep(300);
- if (wait_for(I915_READ(PCH_PP_STATUS) & PP_ON, 5000, 10))
+ if (wait_for((I915_READ(PCH_PP_STATUS) & idle_on_mask) == idle_on_mask,
+ 5000))
DRM_ERROR("panel on wait timed out: 0x%08x\n",
I915_READ(PCH_PP_STATUS));
- pp &= ~(PANEL_UNLOCK_REGS | EDP_FORCE_VDD);
pp |= PANEL_POWER_RESET; /* restore panel reset bit */
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
+
+ return false;
}
static void ironlake_edp_panel_off (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 pp;
+ u32 pp, idle_off_mask = PP_ON | PP_SEQUENCE_MASK |
+ PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK;
pp = I915_READ(PCH_PP_CONTROL);
@@ -796,15 +841,20 @@ static void ironlake_edp_panel_off (struct drm_device *dev)
pp &= ~POWER_TARGET_ON;
I915_WRITE(PCH_PP_CONTROL, pp);
+ POSTING_READ(PCH_PP_CONTROL);
- if (wait_for((I915_READ(PCH_PP_STATUS) & PP_ON) == 0, 5000, 10))
+ if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000))
DRM_ERROR("panel off wait timed out: 0x%08x\n",
I915_READ(PCH_PP_STATUS));
- /* Make sure VDD is enabled so DP AUX will work */
- pp |= EDP_FORCE_VDD | PANEL_POWER_RESET; /* restore panel reset bit */
+ pp |= PANEL_POWER_RESET; /* restore panel reset bit */
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
+
+ /* Ouch. We need to wait here for some panels, like Dell e6510
+ * https://bugs.freedesktop.org/show_bug.cgi?id=29278i
+ */
+ msleep(300);
}
static void ironlake_edp_backlight_on (struct drm_device *dev)
@@ -813,6 +863,13 @@ static void ironlake_edp_backlight_on (struct drm_device *dev)
u32 pp;
DRM_DEBUG_KMS("\n");
+ /*
+ * If we enable the backlight right away following a panel power
+ * on, we may see slight flicker as the panel syncs with the eDP
+ * link. So delay a bit to make sure the image is solid before
+ * allowing it to appear.
+ */
+ msleep(300);
pp = I915_READ(PCH_PP_CONTROL);
pp |= EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
@@ -837,8 +894,10 @@ static void ironlake_edp_pll_on(struct drm_encoder *encoder)
DRM_DEBUG_KMS("\n");
dpa_ctl = I915_READ(DP_A);
- dpa_ctl &= ~DP_PLL_ENABLE;
+ dpa_ctl |= DP_PLL_ENABLE;
I915_WRITE(DP_A, dpa_ctl);
+ POSTING_READ(DP_A);
+ udelay(200);
}
static void ironlake_edp_pll_off(struct drm_encoder *encoder)
@@ -848,8 +907,9 @@ static void ironlake_edp_pll_off(struct drm_encoder *encoder)
u32 dpa_ctl;
dpa_ctl = I915_READ(DP_A);
- dpa_ctl |= DP_PLL_ENABLE;
+ dpa_ctl &= ~DP_PLL_ENABLE;
I915_WRITE(DP_A, dpa_ctl);
+ POSTING_READ(DP_A);
udelay(200);
}
@@ -857,29 +917,31 @@ static void intel_dp_prepare(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t dp_reg = I915_READ(intel_dp->output_reg);
- if (IS_eDP(intel_dp)) {
+ if (is_edp(intel_dp)) {
ironlake_edp_backlight_off(dev);
- ironlake_edp_panel_on(dev);
- ironlake_edp_pll_on(encoder);
+ ironlake_edp_panel_on(intel_dp);
+ if (!is_pch_edp(intel_dp))
+ ironlake_edp_pll_on(encoder);
+ else
+ ironlake_edp_pll_off(encoder);
}
- if (dp_reg & DP_PORT_EN)
- intel_dp_link_down(intel_dp);
+ intel_dp_link_down(intel_dp);
}
static void intel_dp_commit(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t dp_reg = I915_READ(intel_dp->output_reg);
- if (!(dp_reg & DP_PORT_EN)) {
- intel_dp_link_train(intel_dp);
- }
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
+ intel_dp_start_link_train(intel_dp);
+
+ if (is_edp(intel_dp))
+ ironlake_edp_panel_on(intel_dp);
+
+ intel_dp_complete_link_train(intel_dp);
+
+ if (is_edp(intel_dp))
ironlake_edp_backlight_on(dev);
}
@@ -892,22 +954,22 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
uint32_t dp_reg = I915_READ(intel_dp->output_reg);
if (mode != DRM_MODE_DPMS_ON) {
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) {
+ if (is_edp(intel_dp))
ironlake_edp_backlight_off(dev);
+ intel_dp_link_down(intel_dp);
+ if (is_edp(intel_dp))
ironlake_edp_panel_off(dev);
- }
- if (dp_reg & DP_PORT_EN)
- intel_dp_link_down(intel_dp);
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
+ if (is_edp(intel_dp) && !is_pch_edp(intel_dp))
ironlake_edp_pll_off(encoder);
} else {
+ if (is_edp(intel_dp))
+ ironlake_edp_panel_on(intel_dp);
if (!(dp_reg & DP_PORT_EN)) {
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
- ironlake_edp_panel_on(dev);
- intel_dp_link_train(intel_dp);
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
- ironlake_edp_backlight_on(dev);
+ intel_dp_start_link_train(intel_dp);
+ intel_dp_complete_link_train(intel_dp);
}
+ if (is_edp(intel_dp))
+ ironlake_edp_backlight_on(dev);
}
intel_dp->dpms_mode = mode;
}
@@ -917,14 +979,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
* link status information
*/
static bool
-intel_dp_get_link_status(struct intel_dp *intel_dp,
- uint8_t link_status[DP_LINK_STATUS_SIZE])
+intel_dp_get_link_status(struct intel_dp *intel_dp)
{
int ret;
ret = intel_dp_aux_native_read(intel_dp,
DP_LANE0_1_STATUS,
- link_status, DP_LINK_STATUS_SIZE);
+ intel_dp->link_status, DP_LINK_STATUS_SIZE);
if (ret != DP_LINK_STATUS_SIZE)
return false;
return true;
@@ -999,18 +1060,15 @@ intel_dp_pre_emphasis_max(uint8_t voltage_swing)
}
static void
-intel_get_adjust_train(struct intel_dp *intel_dp,
- uint8_t link_status[DP_LINK_STATUS_SIZE],
- int lane_count,
- uint8_t train_set[4])
+intel_get_adjust_train(struct intel_dp *intel_dp)
{
uint8_t v = 0;
uint8_t p = 0;
int lane;
- for (lane = 0; lane < lane_count; lane++) {
- uint8_t this_v = intel_get_adjust_request_voltage(link_status, lane);
- uint8_t this_p = intel_get_adjust_request_pre_emphasis(link_status, lane);
+ for (lane = 0; lane < intel_dp->lane_count; lane++) {
+ uint8_t this_v = intel_get_adjust_request_voltage(intel_dp->link_status, lane);
+ uint8_t this_p = intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane);
if (this_v > v)
v = this_v;
@@ -1025,15 +1083,25 @@ intel_get_adjust_train(struct intel_dp *intel_dp,
p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
for (lane = 0; lane < 4; lane++)
- train_set[lane] = v | p;
+ intel_dp->train_set[lane] = v | p;
}
static uint32_t
-intel_dp_signal_levels(uint8_t train_set, int lane_count)
+intel_dp_signal_levels(struct intel_dp *intel_dp)
{
- uint32_t signal_levels = 0;
+ struct drm_device *dev = intel_dp->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t signal_levels = 0;
+ u8 train_set = intel_dp->train_set[0];
+ u32 vswing = train_set & DP_TRAIN_VOLTAGE_SWING_MASK;
+ u32 preemphasis = train_set & DP_TRAIN_PRE_EMPHASIS_MASK;
+
+ if (is_edp(intel_dp)) {
+ vswing = dev_priv->edp.vswing;
+ preemphasis = dev_priv->edp.preemphasis;
+ }
- switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ switch (vswing) {
case DP_TRAIN_VOLTAGE_SWING_400:
default:
signal_levels |= DP_VOLTAGE_0_4;
@@ -1048,7 +1116,7 @@ intel_dp_signal_levels(uint8_t train_set, int lane_count)
signal_levels |= DP_VOLTAGE_1_2;
break;
}
- switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
+ switch (preemphasis) {
case DP_TRAIN_PRE_EMPHASIS_0:
default:
signal_levels |= DP_PRE_EMPHASIS_0;
@@ -1116,18 +1184,18 @@ intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count
DP_LANE_CHANNEL_EQ_DONE|\
DP_LANE_SYMBOL_LOCKED)
static bool
-intel_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
+intel_channel_eq_ok(struct intel_dp *intel_dp)
{
uint8_t lane_align;
uint8_t lane_status;
int lane;
- lane_align = intel_dp_link_status(link_status,
+ lane_align = intel_dp_link_status(intel_dp->link_status,
DP_LANE_ALIGN_STATUS_UPDATED);
if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
return false;
- for (lane = 0; lane < lane_count; lane++) {
- lane_status = intel_get_lane_status(link_status, lane);
+ for (lane = 0; lane < intel_dp->lane_count; lane++) {
+ lane_status = intel_get_lane_status(intel_dp->link_status, lane);
if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
return false;
}
@@ -1135,159 +1203,194 @@ intel_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
}
static bool
+intel_dp_aux_handshake_required(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (is_edp(intel_dp) && dev_priv->no_aux_handshake)
+ return false;
+
+ return true;
+}
+
+static bool
intel_dp_set_link_train(struct intel_dp *intel_dp,
uint32_t dp_reg_value,
- uint8_t dp_train_pat,
- uint8_t train_set[4])
+ uint8_t dp_train_pat)
{
- struct drm_device *dev = intel_dp->base.enc.dev;
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
I915_WRITE(intel_dp->output_reg, dp_reg_value);
POSTING_READ(intel_dp->output_reg);
+ if (!intel_dp_aux_handshake_required(intel_dp))
+ return true;
+
intel_dp_aux_native_write_1(intel_dp,
DP_TRAINING_PATTERN_SET,
dp_train_pat);
ret = intel_dp_aux_native_write(intel_dp,
- DP_TRAINING_LANE0_SET, train_set, 4);
+ DP_TRAINING_LANE0_SET,
+ intel_dp->train_set, 4);
if (ret != 4)
return false;
return true;
}
+/* Enable corresponding port and start training pattern 1 */
static void
-intel_dp_link_train(struct intel_dp *intel_dp)
+intel_dp_start_link_train(struct intel_dp *intel_dp)
{
- struct drm_device *dev = intel_dp->base.enc.dev;
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- uint8_t train_set[4];
- uint8_t link_status[DP_LINK_STATUS_SIZE];
+ struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
int i;
uint8_t voltage;
bool clock_recovery = false;
- bool channel_eq = false;
int tries;
u32 reg;
uint32_t DP = intel_dp->DP;
- struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.enc.crtc);
/* Enable output, wait for it to become active */
I915_WRITE(intel_dp->output_reg, intel_dp->DP);
POSTING_READ(intel_dp->output_reg);
intel_wait_for_vblank(dev, intel_crtc->pipe);
- /* Write the link configuration data */
- intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
- intel_dp->link_configuration,
- DP_LINK_CONFIGURATION_SIZE);
+ if (intel_dp_aux_handshake_required(intel_dp))
+ /* Write the link configuration data */
+ intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
+ intel_dp->link_configuration,
+ DP_LINK_CONFIGURATION_SIZE);
DP |= DP_PORT_EN;
- if (HAS_PCH_CPT(dev) && !IS_eDP(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
DP &= ~DP_LINK_TRAIN_MASK_CPT;
else
DP &= ~DP_LINK_TRAIN_MASK;
- memset(train_set, 0, 4);
+ memset(intel_dp->train_set, 0, 4);
voltage = 0xff;
tries = 0;
clock_recovery = false;
for (;;) {
- /* Use train_set[0] to set the voltage and pre emphasis values */
+ /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
uint32_t signal_levels;
- if (IS_GEN6(dev) && IS_eDP(intel_dp)) {
- signal_levels = intel_gen6_edp_signal_levels(train_set[0]);
+ if (IS_GEN6(dev) && is_edp(intel_dp)) {
+ signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
} else {
- signal_levels = intel_dp_signal_levels(train_set[0], intel_dp->lane_count);
+ signal_levels = intel_dp_signal_levels(intel_dp);
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
}
- if (HAS_PCH_CPT(dev) && !IS_eDP(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_PAT_1_CPT;
else
reg = DP | DP_LINK_TRAIN_PAT_1;
if (!intel_dp_set_link_train(intel_dp, reg,
- DP_TRAINING_PATTERN_1, train_set))
+ DP_TRAINING_PATTERN_1))
break;
/* Set training pattern 1 */
- udelay(100);
- if (!intel_dp_get_link_status(intel_dp, link_status))
+ udelay(500);
+ if (intel_dp_aux_handshake_required(intel_dp)) {
break;
+ } else {
+ if (!intel_dp_get_link_status(intel_dp))
+ break;
- if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) {
- clock_recovery = true;
- break;
- }
-
- /* Check to see if we've tried the max voltage */
- for (i = 0; i < intel_dp->lane_count; i++)
- if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+ if (intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
+ clock_recovery = true;
break;
- if (i == intel_dp->lane_count)
- break;
+ }
- /* Check to see if we've tried the same voltage 5 times */
- if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
- ++tries;
- if (tries == 5)
+ /* Check to see if we've tried the max voltage */
+ for (i = 0; i < intel_dp->lane_count; i++)
+ if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+ break;
+ if (i == intel_dp->lane_count)
break;
- } else
- tries = 0;
- voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
- /* Compute new train_set as requested by target */
- intel_get_adjust_train(intel_dp, link_status, intel_dp->lane_count, train_set);
+ /* Check to see if we've tried the same voltage 5 times */
+ if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+ ++tries;
+ if (tries == 5)
+ break;
+ } else
+ tries = 0;
+ voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+ /* Compute new intel_dp->train_set as requested by target */
+ intel_get_adjust_train(intel_dp);
+ }
}
+ intel_dp->DP = DP;
+}
+
+static void
+intel_dp_complete_link_train(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ bool channel_eq = false;
+ int tries;
+ u32 reg;
+ uint32_t DP = intel_dp->DP;
+
/* channel equalization */
tries = 0;
channel_eq = false;
for (;;) {
- /* Use train_set[0] to set the voltage and pre emphasis values */
+ /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
uint32_t signal_levels;
- if (IS_GEN6(dev) && IS_eDP(intel_dp)) {
- signal_levels = intel_gen6_edp_signal_levels(train_set[0]);
+ if (IS_GEN6(dev) && is_edp(intel_dp)) {
+ signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
} else {
- signal_levels = intel_dp_signal_levels(train_set[0], intel_dp->lane_count);
+ signal_levels = intel_dp_signal_levels(intel_dp);
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
}
- if (HAS_PCH_CPT(dev) && !IS_eDP(intel_dp))
+ if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_PAT_2_CPT;
else
reg = DP | DP_LINK_TRAIN_PAT_2;
/* channel eq pattern */
if (!intel_dp_set_link_train(intel_dp, reg,
- DP_TRAINING_PATTERN_2, train_set))
+ DP_TRAINING_PATTERN_2))
break;
- udelay(400);
- if (!intel_dp_get_link_status(intel_dp, link_status))
- break;
+ udelay(500);
- if (intel_channel_eq_ok(link_status, intel_dp->lane_count)) {
- channel_eq = true;
+ if (!intel_dp_aux_handshake_required(intel_dp)) {
break;
- }
+ } else {
+ if (!intel_dp_get_link_status(intel_dp))
+ break;
- /* Try 5 times */
- if (tries > 5)
- break;
+ if (intel_channel_eq_ok(intel_dp)) {
+ channel_eq = true;
+ break;
+ }
- /* Compute new train_set as requested by target */
- intel_get_adjust_train(intel_dp, link_status, intel_dp->lane_count, train_set);
- ++tries;
- }
+ /* Try 5 times */
+ if (tries > 5)
+ break;
- if (HAS_PCH_CPT(dev) && !IS_eDP(intel_dp))
+ /* Compute new intel_dp->train_set as requested by target */
+ intel_get_adjust_train(intel_dp);
+ ++tries;
+ }
+ }
+ if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_OFF_CPT;
else
reg = DP | DP_LINK_TRAIN_OFF;
@@ -1301,32 +1404,31 @@ intel_dp_link_train(struct intel_dp *intel_dp)
static void
intel_dp_link_down(struct intel_dp *intel_dp)
{
- struct drm_device *dev = intel_dp->base.enc.dev;
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t DP = intel_dp->DP;
DRM_DEBUG_KMS("\n");
- if (IS_eDP(intel_dp)) {
+ if (is_edp(intel_dp)) {
DP &= ~DP_PLL_ENABLE;
I915_WRITE(intel_dp->output_reg, DP);
POSTING_READ(intel_dp->output_reg);
udelay(100);
}
- if (HAS_PCH_CPT(dev) && !IS_eDP(intel_dp)) {
+ if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) {
DP &= ~DP_LINK_TRAIN_MASK_CPT;
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
- POSTING_READ(intel_dp->output_reg);
} else {
DP &= ~DP_LINK_TRAIN_MASK;
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
- POSTING_READ(intel_dp->output_reg);
}
+ POSTING_READ(intel_dp->output_reg);
- udelay(17000);
+ msleep(17);
- if (IS_eDP(intel_dp))
+ if (is_edp(intel_dp))
DP |= DP_LINK_TRAIN_OFF;
I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
POSTING_READ(intel_dp->output_reg);
@@ -1344,32 +1446,34 @@ intel_dp_link_down(struct intel_dp *intel_dp)
static void
intel_dp_check_link_status(struct intel_dp *intel_dp)
{
- uint8_t link_status[DP_LINK_STATUS_SIZE];
-
- if (!intel_dp->base.enc.crtc)
+ if (!intel_dp->base.base.crtc)
return;
- if (!intel_dp_get_link_status(intel_dp, link_status)) {
+ if (!intel_dp_get_link_status(intel_dp)) {
intel_dp_link_down(intel_dp);
return;
}
- if (!intel_channel_eq_ok(link_status, intel_dp->lane_count))
- intel_dp_link_train(intel_dp);
+ if (!intel_channel_eq_ok(intel_dp)) {
+ intel_dp_start_link_train(intel_dp);
+ intel_dp_complete_link_train(intel_dp);
+ }
}
static enum drm_connector_status
-ironlake_dp_detect(struct drm_connector *connector)
+ironlake_dp_detect(struct intel_dp *intel_dp)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
enum drm_connector_status status;
+ /* Can't disconnect eDP */
+ if (is_edp(intel_dp))
+ return connector_status_connected;
+
status = connector_status_disconnected;
if (intel_dp_aux_native_read(intel_dp,
0x000, intel_dp->dpcd,
- sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd))
- {
+ sizeof (intel_dp->dpcd))
+ == sizeof(intel_dp->dpcd)) {
if (intel_dp->dpcd[0] != 0)
status = connector_status_connected;
}
@@ -1378,26 +1482,13 @@ ironlake_dp_detect(struct drm_connector *connector)
return status;
}
-/**
- * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
- *
- * \return true if DP port is connected.
- * \return false if DP port is disconnected.
- */
static enum drm_connector_status
-intel_dp_detect(struct drm_connector *connector, bool force)
+g4x_dp_detect(struct intel_dp *intel_dp)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- struct drm_device *dev = intel_dp->base.enc.dev;
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t temp, bit;
enum drm_connector_status status;
-
- intel_dp->has_audio = false;
-
- if (HAS_PCH_SPLIT(dev))
- return ironlake_dp_detect(connector);
+ uint32_t temp, bit;
switch (intel_dp->output_reg) {
case DP_B:
@@ -1419,31 +1510,66 @@ intel_dp_detect(struct drm_connector *connector, bool force)
return connector_status_disconnected;
status = connector_status_disconnected;
- if (intel_dp_aux_native_read(intel_dp,
- 0x000, intel_dp->dpcd,
+ if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd,
sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd))
{
if (intel_dp->dpcd[0] != 0)
status = connector_status_connected;
}
- return status;
+
+ return bit;
+}
+
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
+ *
+ * \return true if DP port is connected.
+ * \return false if DP port is disconnected.
+ */
+static enum drm_connector_status
+intel_dp_detect(struct drm_connector *connector, bool force)
+{
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct drm_device *dev = intel_dp->base.base.dev;
+ enum drm_connector_status status;
+ struct edid *edid = NULL;
+
+ intel_dp->has_audio = false;
+
+ if (HAS_PCH_SPLIT(dev))
+ status = ironlake_dp_detect(intel_dp);
+ else
+ status = g4x_dp_detect(intel_dp);
+ if (status != connector_status_connected)
+ return status;
+
+ if (intel_dp->force_audio) {
+ intel_dp->has_audio = intel_dp->force_audio > 0;
+ } else {
+ edid = drm_get_edid(connector, &intel_dp->adapter);
+ if (edid) {
+ intel_dp->has_audio = drm_detect_monitor_audio(edid);
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
+ }
+ }
+
+ return connector_status_connected;
}
static int intel_dp_get_modes(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- struct drm_device *dev = intel_dp->base.enc.dev;
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
/* We should parse the EDID data and find out if it has an audio sink
*/
- ret = intel_ddc_get_modes(connector, intel_dp->base.ddc_bus);
+ ret = intel_ddc_get_modes(connector, &intel_dp->adapter);
if (ret) {
- if ((IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) &&
- !dev_priv->panel_fixed_mode) {
+ if (is_edp(intel_dp) && !dev_priv->panel_fixed_mode) {
struct drm_display_mode *newmode;
list_for_each_entry(newmode, &connector->probed_modes,
head) {
@@ -1459,7 +1585,7 @@ static int intel_dp_get_modes(struct drm_connector *connector)
}
/* if eDP has no EDID, try to use fixed panel mode from VBT */
- if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) {
+ if (is_edp(intel_dp)) {
if (dev_priv->panel_fixed_mode != NULL) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
@@ -1470,6 +1596,46 @@ static int intel_dp_get_modes(struct drm_connector *connector)
return 0;
}
+static int
+intel_dp_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ int ret;
+
+ ret = drm_connector_property_set_value(connector, property, val);
+ if (ret)
+ return ret;
+
+ if (property == intel_dp->force_audio_property) {
+ if (val == intel_dp->force_audio)
+ return 0;
+
+ intel_dp->force_audio = val;
+
+ if (val > 0 && intel_dp->has_audio)
+ return 0;
+ if (val < 0 && !intel_dp->has_audio)
+ return 0;
+
+ intel_dp->has_audio = val > 0;
+ goto done;
+ }
+
+ return -EINVAL;
+
+done:
+ if (intel_dp->base.base.crtc) {
+ struct drm_crtc *crtc = intel_dp->base.base.crtc;
+ drm_crtc_helper_set_mode(crtc, &crtc->mode,
+ crtc->x, crtc->y,
+ crtc->fb);
+ }
+
+ return 0;
+}
+
static void
intel_dp_destroy (struct drm_connector *connector)
{
@@ -1478,6 +1644,15 @@ intel_dp_destroy (struct drm_connector *connector)
kfree(connector);
}
+static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ i2c_del_adapter(&intel_dp->adapter);
+ drm_encoder_cleanup(encoder);
+ kfree(intel_dp);
+}
+
static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
.dpms = intel_dp_dpms,
.mode_fixup = intel_dp_mode_fixup,
@@ -1490,20 +1665,21 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_dp_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = intel_dp_set_property,
.destroy = intel_dp_destroy,
};
static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = {
.get_modes = intel_dp_get_modes,
.mode_valid = intel_dp_mode_valid,
- .best_encoder = intel_attached_encoder,
+ .best_encoder = intel_best_encoder,
};
static const struct drm_encoder_funcs intel_dp_enc_funcs = {
- .destroy = intel_encoder_destroy,
+ .destroy = intel_dp_encoder_destroy,
};
-void
+static void
intel_dp_hot_plug(struct intel_encoder *intel_encoder)
{
struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base);
@@ -1554,6 +1730,20 @@ bool intel_dpd_is_edp(struct drm_device *dev)
return false;
}
+static void
+intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+
+ intel_dp->force_audio_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE, "force_audio", 2);
+ if (intel_dp->force_audio_property) {
+ intel_dp->force_audio_property->values[0] = -1;
+ intel_dp->force_audio_property->values[1] = 1;
+ drm_connector_attach_property(connector, intel_dp->force_audio_property, 0);
+ }
+}
+
void
intel_dp_init(struct drm_device *dev, int output_reg)
{
@@ -1580,7 +1770,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
if (intel_dpd_is_edp(dev))
intel_dp->is_pch_edp = true;
- if (output_reg == DP_A || IS_PCH_eDP(intel_dp)) {
+ if (output_reg == DP_A || is_pch_edp(intel_dp)) {
type = DRM_MODE_CONNECTOR_eDP;
intel_encoder->type = INTEL_OUTPUT_EDP;
} else {
@@ -1601,7 +1791,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
else if (output_reg == DP_D || output_reg == PCH_DP_D)
intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
- if (IS_eDP(intel_dp))
+ if (is_edp(intel_dp))
intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
@@ -1612,12 +1802,11 @@ intel_dp_init(struct drm_device *dev, int output_reg)
intel_dp->has_audio = false;
intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
- drm_encoder_init(dev, &intel_encoder->enc, &intel_dp_enc_funcs,
+ drm_encoder_init(dev, &intel_encoder->base, &intel_dp_enc_funcs,
DRM_MODE_ENCODER_TMDS);
- drm_encoder_helper_add(&intel_encoder->enc, &intel_dp_helper_funcs);
+ drm_encoder_helper_add(&intel_encoder->base, &intel_dp_helper_funcs);
- drm_mode_connector_attach_encoder(&intel_connector->base,
- &intel_encoder->enc);
+ intel_connector_attach_encoder(intel_connector, intel_encoder);
drm_sysfs_connector_add(connector);
/* Set up the DDC bus. */
@@ -1647,10 +1836,29 @@ intel_dp_init(struct drm_device *dev, int output_reg)
intel_dp_i2c_init(intel_dp, intel_connector, name);
- intel_encoder->ddc_bus = &intel_dp->adapter;
+ /* Cache some DPCD data in the eDP case */
+ if (is_edp(intel_dp)) {
+ int ret;
+ bool was_on;
+
+ was_on = ironlake_edp_panel_on(intel_dp);
+ ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV,
+ intel_dp->dpcd,
+ sizeof(intel_dp->dpcd));
+ if (ret == sizeof(intel_dp->dpcd)) {
+ if (intel_dp->dpcd[0] >= 0x11)
+ dev_priv->no_aux_handshake = intel_dp->dpcd[3] &
+ DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
+ } else {
+ DRM_ERROR("failed to retrieve link info\n");
+ }
+ if (!was_on)
+ ironlake_edp_panel_off(dev);
+ }
+
intel_encoder->hot_plug = intel_dp_hot_plug;
- if (output_reg == DP_A || IS_PCH_eDP(intel_dp)) {
+ if (is_edp(intel_dp)) {
/* initialize panel mode from VBT if available for eDP */
if (dev_priv->lfp_lvds_vbt_mode) {
dev_priv->panel_fixed_mode =
@@ -1662,6 +1870,8 @@ intel_dp_init(struct drm_device *dev, int output_reg)
}
}
+ intel_dp_add_properties(intel_dp, connector);
+
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
* generated on the port when a cable is not attached.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 8828b3ac6414..9af9f86a8765 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -26,14 +26,12 @@
#define __INTEL_DRV_H__
#include <linux/i2c.h>
-#include <linux/i2c-id.h>
-#include <linux/i2c-algo-bit.h>
#include "i915_drv.h"
#include "drm_crtc.h"
-
#include "drm_crtc_helper.h"
+#include "drm_fb_helper.h"
-#define wait_for(COND, MS, W) ({ \
+#define _wait_for(COND, MS, W) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
int ret__ = 0; \
while (! (COND)) { \
@@ -41,11 +39,24 @@
ret__ = -ETIMEDOUT; \
break; \
} \
- if (W) msleep(W); \
+ if (W && !in_dbg_master()) msleep(W); \
} \
ret__; \
})
+#define wait_for(COND, MS) _wait_for(COND, MS, 1)
+#define wait_for_atomic(COND, MS) _wait_for(COND, MS, 0)
+
+#define MSLEEP(x) do { \
+ if (in_dbg_master()) \
+ mdelay(x); \
+ else \
+ msleep(x); \
+} while(0)
+
+#define KHz(x) (1000*x)
+#define MHz(x) KHz(1000*x)
+
/*
* Display related stuff
*/
@@ -96,24 +107,39 @@
#define INTEL_DVO_CHIP_TMDS 2
#define INTEL_DVO_CHIP_TVOUT 4
-struct intel_i2c_chan {
- struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */
- u32 reg; /* GPIO reg */
- struct i2c_adapter adapter;
- struct i2c_algo_bit_data algo;
-};
+/* drm_display_mode->private_flags */
+#define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0)
+#define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT)
+
+static inline void
+intel_mode_set_pixel_multiplier(struct drm_display_mode *mode,
+ int multiplier)
+{
+ mode->clock *= multiplier;
+ mode->private_flags |= multiplier;
+}
+
+static inline int
+intel_mode_get_pixel_multiplier(const struct drm_display_mode *mode)
+{
+ return (mode->private_flags & INTEL_MODE_PIXEL_MULTIPLIER_MASK) >> INTEL_MODE_PIXEL_MULTIPLIER_SHIFT;
+}
struct intel_framebuffer {
struct drm_framebuffer base;
struct drm_gem_object *obj;
};
+struct intel_fbdev {
+ struct drm_fb_helper helper;
+ struct intel_framebuffer ifb;
+ struct list_head fbdev_list;
+ struct drm_display_mode *our_mode;
+};
struct intel_encoder {
- struct drm_encoder enc;
+ struct drm_encoder base;
int type;
- struct i2c_adapter *i2c_bus;
- struct i2c_adapter *ddc_bus;
bool load_detect_temp;
bool needs_tv_clock;
void (*hot_plug)(struct intel_encoder *);
@@ -123,32 +149,7 @@ struct intel_encoder {
struct intel_connector {
struct drm_connector base;
-};
-
-struct intel_crtc;
-struct intel_overlay {
- struct drm_device *dev;
- struct intel_crtc *crtc;
- struct drm_i915_gem_object *vid_bo;
- struct drm_i915_gem_object *old_vid_bo;
- int active;
- int pfit_active;
- u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
- u32 color_key;
- u32 brightness, contrast, saturation;
- u32 old_xscale, old_yscale;
- /* register access */
- u32 flip_addr;
- struct drm_i915_gem_object *reg_bo;
- void *virt_addr;
- /* flip handling */
- uint32_t last_flip_req;
- int hw_wedged;
-#define HW_WEDGED 1
-#define NEEDS_WAIT_FOR_FLIP 2
-#define RELEASE_OLD_VID 3
-#define SWITCH_OFF_STAGE_1 4
-#define SWITCH_OFF_STAGE_2 5
+ struct intel_encoder *encoder;
};
struct intel_crtc {
@@ -157,6 +158,7 @@ struct intel_crtc {
enum plane plane;
u8 lut_r[256], lut_g[256], lut_b[256];
int dpms_mode;
+ bool active; /* is the crtc on? independent of the dpms mode */
bool busy; /* is scanout buffer being updated frequently? */
struct timer_list idle_timer;
bool lowfreq_avail;
@@ -168,14 +170,53 @@ struct intel_crtc {
uint32_t cursor_addr;
int16_t cursor_x, cursor_y;
int16_t cursor_width, cursor_height;
- bool cursor_visible, cursor_on;
+ bool cursor_visible;
};
#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
#define to_intel_connector(x) container_of(x, struct intel_connector, base)
-#define enc_to_intel_encoder(x) container_of(x, struct intel_encoder, enc)
+#define to_intel_encoder(x) container_of(x, struct intel_encoder, base)
#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
+#define DIP_TYPE_AVI 0x82
+#define DIP_VERSION_AVI 0x2
+#define DIP_LEN_AVI 13
+
+struct dip_infoframe {
+ uint8_t type; /* HB0 */
+ uint8_t ver; /* HB1 */
+ uint8_t len; /* HB2 - body len, not including checksum */
+ uint8_t ecc; /* Header ECC */
+ uint8_t checksum; /* PB0 */
+ union {
+ struct {
+ /* PB1 - Y 6:5, A 4:4, B 3:2, S 1:0 */
+ uint8_t Y_A_B_S;
+ /* PB2 - C 7:6, M 5:4, R 3:0 */
+ uint8_t C_M_R;
+ /* PB3 - ITC 7:7, EC 6:4, Q 3:2, SC 1:0 */
+ uint8_t ITC_EC_Q_SC;
+ /* PB4 - VIC 6:0 */
+ uint8_t VIC;
+ /* PB5 - PR 3:0 */
+ uint8_t PR;
+ /* PB6 to PB13 */
+ uint16_t top_bar_end;
+ uint16_t bottom_bar_start;
+ uint16_t left_bar_end;
+ uint16_t right_bar_start;
+ } avi;
+ uint8_t payload[27];
+ } __attribute__ ((packed)) body;
+} __attribute__((packed));
+
+static inline struct drm_crtc *
+intel_get_crtc_for_pipe(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ return dev_priv->pipe_to_crtc_mapping[pipe];
+}
+
struct intel_unpin_work {
struct work_struct work;
struct drm_device *dev;
@@ -186,16 +227,12 @@ struct intel_unpin_work {
bool enable_stall_check;
};
-struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg,
- const char *name);
-void intel_i2c_destroy(struct i2c_adapter *adapter);
int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
-extern bool intel_ddc_probe(struct intel_encoder *intel_encoder);
-void intel_i2c_quirk_set(struct drm_device *dev, bool enable);
-void intel_i2c_reset_gmbus(struct drm_device *dev);
+extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus);
extern void intel_crt_init(struct drm_device *dev);
extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
+void intel_dip_infoframe_csum(struct dip_infoframe *avi_if);
extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
extern void intel_dvo_init(struct drm_device *dev);
extern void intel_tv_init(struct drm_device *dev);
@@ -205,32 +242,41 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg);
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
-extern bool intel_pch_has_edp(struct drm_crtc *crtc);
extern bool intel_dpd_is_edp(struct drm_device *dev);
extern void intel_edp_link_config (struct intel_encoder *, int *, int *);
+extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);
-
+/* intel_panel.c */
extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode);
extern void intel_pch_panel_fitting(struct drm_device *dev,
int fitting_mode,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
+extern u32 intel_panel_get_backlight(struct drm_device *dev);
+extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
-extern int intel_panel_fitter_pipe (struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare (struct drm_encoder *encoder);
extern void intel_encoder_commit (struct drm_encoder *encoder);
extern void intel_encoder_destroy(struct drm_encoder *encoder);
-extern struct drm_encoder *intel_attached_encoder(struct drm_connector *connector);
+static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
+{
+ return to_intel_connector(connector)->encoder;
+}
+
+extern void intel_connector_attach_encoder(struct intel_connector *connector,
+ struct intel_encoder *encoder);
+extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
struct drm_crtc *crtc);
int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern void intel_wait_for_vblank(struct drm_device *dev, int pipe);
-extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
+extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector,
struct drm_display_mode *mode,
@@ -252,7 +298,8 @@ extern void ironlake_enable_drps(struct drm_device *dev);
extern void ironlake_disable_drps(struct drm_device *dev);
extern int intel_pin_and_fence_fb_obj(struct drm_device *dev,
- struct drm_gem_object *obj);
+ struct drm_gem_object *obj,
+ bool pipelined);
extern int intel_framebuffer_init(struct drm_device *dev,
struct intel_framebuffer *ifb,
@@ -267,9 +314,8 @@ extern void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
extern void intel_setup_overlay(struct drm_device *dev);
extern void intel_cleanup_overlay(struct drm_device *dev);
-extern int intel_overlay_switch_off(struct intel_overlay *overlay);
-extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
- int interruptible);
+extern int intel_overlay_switch_off(struct intel_overlay *overlay,
+ bool interruptible);
extern int intel_overlay_put_image(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int intel_overlay_attrs(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index 7c9ec1472d46..ea373283c93b 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -72,7 +72,7 @@ static const struct intel_dvo_device intel_dvo_devices[] = {
.name = "ch7017",
.dvo_reg = DVOC,
.slave_addr = 0x75,
- .gpio = GPIOE,
+ .gpio = GMBUS_PORT_DPB,
.dev_ops = &ch7017_ops,
}
};
@@ -88,7 +88,13 @@ struct intel_dvo {
static struct intel_dvo *enc_to_intel_dvo(struct drm_encoder *encoder)
{
- return container_of(enc_to_intel_encoder(encoder), struct intel_dvo, base);
+ return container_of(encoder, struct intel_dvo, base.base);
+}
+
+static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector)
+{
+ return container_of(intel_attached_encoder(connector),
+ struct intel_dvo, base);
}
static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
@@ -112,8 +118,7 @@ static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
static int intel_dvo_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
+ struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
@@ -224,23 +229,22 @@ static void intel_dvo_mode_set(struct drm_encoder *encoder,
static enum drm_connector_status
intel_dvo_detect(struct drm_connector *connector, bool force)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
-
+ struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev);
}
static int intel_dvo_get_modes(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
+ struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
/* We should probably have an i2c driver get_modes function for those
* devices which will have a fixed set of modes determined by the chip
* (TV-out, for example), but for now with just TMDS and LVDS,
* that's not the case.
*/
- intel_ddc_get_modes(connector, intel_dvo->base.ddc_bus);
+ intel_ddc_get_modes(connector,
+ &dev_priv->gmbus[GMBUS_PORT_DPC].adapter);
if (!list_empty(&connector->probed_modes))
return 1;
@@ -281,7 +285,7 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = {
static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
.mode_valid = intel_dvo_mode_valid,
.get_modes = intel_dvo_get_modes,
- .best_encoder = intel_attached_encoder,
+ .best_encoder = intel_best_encoder,
};
static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
@@ -311,8 +315,7 @@ intel_dvo_get_current_mode(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
+ struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
uint32_t dvo_val = I915_READ(intel_dvo->dev.dvo_reg);
struct drm_display_mode *mode = NULL;
@@ -323,7 +326,7 @@ intel_dvo_get_current_mode(struct drm_connector *connector)
struct drm_crtc *crtc;
int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0;
- crtc = intel_get_crtc_from_pipe(dev, pipe);
+ crtc = intel_get_crtc_for_pipe(dev, pipe);
if (crtc) {
mode = intel_crtc_mode_get(dev, crtc);
if (mode) {
@@ -341,11 +344,10 @@ intel_dvo_get_current_mode(struct drm_connector *connector)
void intel_dvo_init(struct drm_device *dev)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *intel_encoder;
struct intel_dvo *intel_dvo;
struct intel_connector *intel_connector;
- struct i2c_adapter *i2cbus = NULL;
- int ret = 0;
int i;
int encoder_type = DRM_MODE_ENCODER_NONE;
@@ -360,16 +362,14 @@ void intel_dvo_init(struct drm_device *dev)
}
intel_encoder = &intel_dvo->base;
-
- /* Set up the DDC bus */
- intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D");
- if (!intel_encoder->ddc_bus)
- goto free_intel;
+ drm_encoder_init(dev, &intel_encoder->base,
+ &intel_dvo_enc_funcs, encoder_type);
/* Now, try to find a controller */
for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
struct drm_connector *connector = &intel_connector->base;
const struct intel_dvo_device *dvo = &intel_dvo_devices[i];
+ struct i2c_adapter *i2c;
int gpio;
/* Allow the I2C driver info to specify the GPIO to be used in
@@ -379,24 +379,18 @@ void intel_dvo_init(struct drm_device *dev)
if (dvo->gpio != 0)
gpio = dvo->gpio;
else if (dvo->type == INTEL_DVO_CHIP_LVDS)
- gpio = GPIOB;
+ gpio = GMBUS_PORT_SSC;
else
- gpio = GPIOE;
+ gpio = GMBUS_PORT_DPB;
/* Set up the I2C bus necessary for the chip we're probing.
* It appears that everything is on GPIOE except for panels
* on i830 laptops, which are on GPIOB (DVOA).
*/
- if (i2cbus != NULL)
- intel_i2c_destroy(i2cbus);
- if (!(i2cbus = intel_i2c_create(dev, gpio,
- gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) {
- continue;
- }
+ i2c = &dev_priv->gmbus[gpio].adapter;
intel_dvo->dev = *dvo;
- ret = dvo->dev_ops->init(&intel_dvo->dev, i2cbus);
- if (!ret)
+ if (!dvo->dev_ops->init(&intel_dvo->dev, i2c))
continue;
intel_encoder->type = INTEL_OUTPUT_DVO;
@@ -427,13 +421,10 @@ void intel_dvo_init(struct drm_device *dev)
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
- drm_encoder_init(dev, &intel_encoder->enc,
- &intel_dvo_enc_funcs, encoder_type);
- drm_encoder_helper_add(&intel_encoder->enc,
+ drm_encoder_helper_add(&intel_encoder->base,
&intel_dvo_helper_funcs);
- drm_mode_connector_attach_encoder(&intel_connector->base,
- &intel_encoder->enc);
+ intel_connector_attach_encoder(intel_connector, intel_encoder);
if (dvo->type == INTEL_DVO_CHIP_LVDS) {
/* For our LVDS chipsets, we should hopefully be able
* to dig the fixed panel mode out of the BIOS data.
@@ -451,11 +442,7 @@ void intel_dvo_init(struct drm_device *dev)
return;
}
- intel_i2c_destroy(intel_encoder->ddc_bus);
- /* Didn't find a chip, so tear down. */
- if (i2cbus != NULL)
- intel_i2c_destroy(i2cbus);
-free_intel:
+ drm_encoder_cleanup(&intel_encoder->base);
kfree(intel_dvo);
kfree(intel_connector);
}
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index b61966c126d3..af2a1dddc28e 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -44,13 +44,6 @@
#include "i915_drm.h"
#include "i915_drv.h"
-struct intel_fbdev {
- struct drm_fb_helper helper;
- struct intel_framebuffer ifb;
- struct list_head fbdev_list;
- struct drm_display_mode *our_mode;
-};
-
static struct fb_ops intelfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var,
@@ -75,7 +68,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
struct drm_gem_object *fbo = NULL;
struct drm_i915_gem_object *obj_priv;
struct device *device = &dev->pdev->dev;
- int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1;
+ int size, ret, mmio_bar = IS_GEN2(dev) ? 1 : 0;
/* we don't do packed 24bpp */
if (sizes->surface_bpp == 24)
@@ -100,19 +93,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
mutex_lock(&dev->struct_mutex);
- ret = intel_pin_and_fence_fb_obj(dev, fbo);
+ /* Flush everything out, we'll be doing GTT only from now on */
+ ret = intel_pin_and_fence_fb_obj(dev, fbo, false);
if (ret) {
DRM_ERROR("failed to pin fb: %d\n", ret);
goto out_unref;
}
- /* Flush everything out, we'll be doing GTT only from now on */
- ret = i915_gem_object_set_to_gtt_domain(fbo, 1);
- if (ret) {
- DRM_ERROR("failed to bind fb: %d.\n", ret);
- goto out_unpin;
- }
-
info = framebuffer_alloc(0, device);
if (!info) {
ret = -ENOMEM;
@@ -142,7 +129,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
goto out_unpin;
}
info->apertures->ranges[0].base = dev->mode_config.fb_base;
- if (IS_I9XX(dev))
+ if (!IS_GEN2(dev))
info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 2);
else
info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0);
@@ -219,8 +206,8 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.fb_probe = intel_fb_find_or_create_single,
};
-int intel_fbdev_destroy(struct drm_device *dev,
- struct intel_fbdev *ifbdev)
+static void intel_fbdev_destroy(struct drm_device *dev,
+ struct intel_fbdev *ifbdev)
{
struct fb_info *info;
struct intel_framebuffer *ifb = &ifbdev->ifb;
@@ -238,11 +225,9 @@ int intel_fbdev_destroy(struct drm_device *dev,
drm_framebuffer_cleanup(&ifb->base);
if (ifb->obj) {
- drm_gem_object_unreference(ifb->obj);
+ drm_gem_object_unreference_unlocked(ifb->obj);
ifb->obj = NULL;
}
-
- return 0;
}
int intel_fbdev_init(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 926934a482ec..0d0273e7b029 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -40,12 +40,76 @@
struct intel_hdmi {
struct intel_encoder base;
u32 sdvox_reg;
+ int ddc_bus;
bool has_hdmi_sink;
+ bool has_audio;
+ int force_audio;
+ struct drm_property *force_audio_property;
};
static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
{
- return container_of(enc_to_intel_encoder(encoder), struct intel_hdmi, base);
+ return container_of(encoder, struct intel_hdmi, base.base);
+}
+
+static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
+{
+ return container_of(intel_attached_encoder(connector),
+ struct intel_hdmi, base);
+}
+
+void intel_dip_infoframe_csum(struct dip_infoframe *avi_if)
+{
+ uint8_t *data = (uint8_t *)avi_if;
+ uint8_t sum = 0;
+ unsigned i;
+
+ avi_if->checksum = 0;
+ avi_if->ecc = 0;
+
+ for (i = 0; i < sizeof(*avi_if); i++)
+ sum += data[i];
+
+ avi_if->checksum = 0x100 - sum;
+}
+
+static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+{
+ struct dip_infoframe avi_if = {
+ .type = DIP_TYPE_AVI,
+ .ver = DIP_VERSION_AVI,
+ .len = DIP_LEN_AVI,
+ };
+ uint32_t *data = (uint32_t *)&avi_if;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+ u32 port;
+ unsigned i;
+
+ if (!intel_hdmi->has_hdmi_sink)
+ return;
+
+ /* XXX first guess at handling video port, is this corrent? */
+ if (intel_hdmi->sdvox_reg == SDVOB)
+ port = VIDEO_DIP_PORT_B;
+ else if (intel_hdmi->sdvox_reg == SDVOC)
+ port = VIDEO_DIP_PORT_C;
+ else
+ return;
+
+ I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
+ VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);
+
+ intel_dip_infoframe_csum(&avi_if);
+ for (i = 0; i < sizeof(avi_if); i += 4) {
+ I915_WRITE(VIDEO_DIP_DATA, *data);
+ data++;
+ }
+
+ I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
+ VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC |
+ VIDEO_DIP_ENABLE_AVI);
}
static void intel_hdmi_mode_set(struct drm_encoder *encoder,
@@ -65,10 +129,13 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
- if (intel_hdmi->has_hdmi_sink) {
+ /* Required on CPT */
+ if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
+ sdvox |= HDMI_MODE_SELECT;
+
+ if (intel_hdmi->has_audio) {
sdvox |= SDVO_AUDIO_ENABLE;
- if (HAS_PCH_CPT(dev))
- sdvox |= HDMI_MODE_SELECT;
+ sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
}
if (intel_crtc->pipe == 1) {
@@ -80,6 +147,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
POSTING_READ(intel_hdmi->sdvox_reg);
+
+ intel_hdmi_set_avi_infoframe(encoder);
}
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
@@ -141,36 +210,85 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
static enum drm_connector_status
intel_hdmi_detect(struct drm_connector *connector, bool force)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
- struct edid *edid = NULL;
+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct edid *edid;
enum drm_connector_status status = connector_status_disconnected;
intel_hdmi->has_hdmi_sink = false;
- edid = drm_get_edid(connector, intel_hdmi->base.ddc_bus);
+ intel_hdmi->has_audio = false;
+ edid = drm_get_edid(connector,
+ &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
status = connector_status_connected;
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;
kfree(edid);
}
+ if (status == connector_status_connected) {
+ if (intel_hdmi->force_audio)
+ intel_hdmi->has_audio = intel_hdmi->force_audio > 0;
+ }
+
return status;
}
static int intel_hdmi_get_modes(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
/* We should parse the EDID data and find out if it's an HDMI sink so
* we can send audio to it.
*/
- return intel_ddc_get_modes(connector, intel_hdmi->base.ddc_bus);
+ return intel_ddc_get_modes(connector,
+ &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
+}
+
+static int
+intel_hdmi_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+ int ret;
+
+ ret = drm_connector_property_set_value(connector, property, val);
+ if (ret)
+ return ret;
+
+ if (property == intel_hdmi->force_audio_property) {
+ if (val == intel_hdmi->force_audio)
+ return 0;
+
+ intel_hdmi->force_audio = val;
+
+ if (val > 0 && intel_hdmi->has_audio)
+ return 0;
+ if (val < 0 && !intel_hdmi->has_audio)
+ return 0;
+
+ intel_hdmi->has_audio = val > 0;
+ goto done;
+ }
+
+ return -EINVAL;
+
+done:
+ if (intel_hdmi->base.base.crtc) {
+ struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
+ drm_crtc_helper_set_mode(crtc, &crtc->mode,
+ crtc->x, crtc->y,
+ crtc->fb);
+ }
+
+ return 0;
}
static void intel_hdmi_destroy(struct drm_connector *connector)
@@ -192,19 +310,34 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_hdmi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = intel_hdmi_set_property,
.destroy = intel_hdmi_destroy,
};
static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
.get_modes = intel_hdmi_get_modes,
.mode_valid = intel_hdmi_mode_valid,
- .best_encoder = intel_attached_encoder,
+ .best_encoder = intel_best_encoder,
};
static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
.destroy = intel_encoder_destroy,
};
+static void
+intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+
+ intel_hdmi->force_audio_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE, "force_audio", 2);
+ if (intel_hdmi->force_audio_property) {
+ intel_hdmi->force_audio_property->values[0] = -1;
+ intel_hdmi->force_audio_property->values[1] = 1;
+ drm_connector_attach_property(connector, intel_hdmi->force_audio_property, 0);
+ }
+}
+
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -224,6 +357,9 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
}
intel_encoder = &intel_hdmi->base;
+ drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
connector = &intel_connector->base;
drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
@@ -239,39 +375,33 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
/* Set up the DDC bus. */
if (sdvox_reg == SDVOB) {
intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
- intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB");
+ intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == SDVOC) {
intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
- intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC");
+ intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMIB) {
intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
- intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOE,
- "HDMIB");
+ intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMIC) {
intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
- intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOD,
- "HDMIC");
+ intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMID) {
intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
- intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOF,
- "HDMID");
+ intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
}
- if (!intel_encoder->ddc_bus)
- goto err_connector;
intel_hdmi->sdvox_reg = sdvox_reg;
- drm_encoder_init(dev, &intel_encoder->enc, &intel_hdmi_enc_funcs,
- DRM_MODE_ENCODER_TMDS);
- drm_encoder_helper_add(&intel_encoder->enc, &intel_hdmi_helper_funcs);
+ drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
+
+ intel_hdmi_add_properties(intel_hdmi, connector);
- drm_mode_connector_attach_encoder(&intel_connector->base,
- &intel_encoder->enc);
+ intel_connector_attach_encoder(intel_connector, intel_encoder);
drm_sysfs_connector_add(connector);
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
@@ -282,13 +412,4 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
u32 temp = I915_READ(PEG_BAND_GAP_DATA);
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
-
- return;
-
-err_connector:
- drm_connector_cleanup(connector);
- kfree(intel_hdmi);
- kfree(intel_connector);
-
- return;
}
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index c2649c7df14c..2be4f728ed0c 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
- * Copyright © 2006-2008 Intel Corporation
+ * Copyright © 2006-2008,2010 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,10 +24,9 @@
*
* Authors:
* Eric Anholt <eric@anholt.net>
+ * Chris Wilson <chris@chris-wilson.co.uk>
*/
#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/i2c-id.h>
#include <linux/i2c-algo-bit.h>
#include "drmP.h"
#include "drm.h"
@@ -35,79 +34,106 @@
#include "i915_drm.h"
#include "i915_drv.h"
-void intel_i2c_quirk_set(struct drm_device *dev, bool enable)
+/* Intel GPIO access functions */
+
+#define I2C_RISEFALL_TIME 20
+
+static inline struct intel_gmbus *
+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)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ if (HAS_PCH_SPLIT(dev))
+ I915_WRITE(PCH_GMBUS0, 0);
+ else
+ I915_WRITE(GMBUS0, 0);
+}
+
+static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
+{
+ u32 val;
/* When using bit bashing for I2C, this bit needs to be set to 1 */
- if (!IS_PINEVIEW(dev))
+ if (!IS_PINEVIEW(dev_priv->dev))
return;
+
+ val = I915_READ(DSPCLK_GATE_D);
if (enable)
- I915_WRITE(DSPCLK_GATE_D,
- I915_READ(DSPCLK_GATE_D) | DPCUNIT_CLOCK_GATE_DISABLE);
+ val |= DPCUNIT_CLOCK_GATE_DISABLE;
else
- I915_WRITE(DSPCLK_GATE_D,
- I915_READ(DSPCLK_GATE_D) & (~DPCUNIT_CLOCK_GATE_DISABLE));
+ val &= ~DPCUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, val);
}
-/*
- * Intel GPIO access functions
- */
+static u32 get_reserved(struct intel_gpio *gpio)
+{
+ struct drm_i915_private *dev_priv = gpio->dev_priv;
+ struct drm_device *dev = dev_priv->dev;
+ u32 reserved = 0;
-#define I2C_RISEFALL_TIME 20
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
+
+ return reserved;
+}
static int get_clock(void *data)
{
- struct intel_i2c_chan *chan = data;
- struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
- u32 val;
-
- val = I915_READ(chan->reg);
- return ((val & GPIO_CLOCK_VAL_IN) != 0);
+ struct intel_gpio *gpio = data;
+ struct drm_i915_private *dev_priv = gpio->dev_priv;
+ u32 reserved = get_reserved(gpio);
+ I915_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK);
+ I915_WRITE(gpio->reg, reserved);
+ return (I915_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0;
}
static int get_data(void *data)
{
- struct intel_i2c_chan *chan = data;
- struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
- u32 val;
-
- val = I915_READ(chan->reg);
- return ((val & GPIO_DATA_VAL_IN) != 0);
+ struct intel_gpio *gpio = data;
+ struct drm_i915_private *dev_priv = gpio->dev_priv;
+ u32 reserved = get_reserved(gpio);
+ I915_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK);
+ I915_WRITE(gpio->reg, reserved);
+ return (I915_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0;
}
static void set_clock(void *data, int state_high)
{
- struct intel_i2c_chan *chan = data;
- struct drm_device *dev = chan->drm_dev;
- struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
- u32 reserved = 0, clock_bits;
-
- /* On most chips, these bits must be preserved in software. */
- if (!IS_I830(dev) && !IS_845G(dev))
- reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
- GPIO_CLOCK_PULLUP_DISABLE);
+ struct intel_gpio *gpio = data;
+ struct drm_i915_private *dev_priv = gpio->dev_priv;
+ u32 reserved = get_reserved(gpio);
+ u32 clock_bits;
if (state_high)
clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
else
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
GPIO_CLOCK_VAL_MASK;
- I915_WRITE(chan->reg, reserved | clock_bits);
- udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+
+ I915_WRITE(gpio->reg, reserved | clock_bits);
+ POSTING_READ(gpio->reg);
}
static void set_data(void *data, int state_high)
{
- struct intel_i2c_chan *chan = data;
- struct drm_device *dev = chan->drm_dev;
- struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
- u32 reserved = 0, data_bits;
-
- /* On most chips, these bits must be preserved in software. */
- if (!IS_I830(dev) && !IS_845G(dev))
- reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
- GPIO_CLOCK_PULLUP_DISABLE);
+ struct intel_gpio *gpio = data;
+ struct drm_i915_private *dev_priv = gpio->dev_priv;
+ u32 reserved = get_reserved(gpio);
+ u32 data_bits;
if (state_high)
data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
@@ -115,109 +141,313 @@ 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(chan->reg, reserved | data_bits);
- udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+ I915_WRITE(gpio->reg, reserved | data_bits);
+ POSTING_READ(gpio->reg);
}
-/* Clears the GMBUS setup. Our driver doesn't make use of the GMBUS I2C
- * engine, but if the BIOS leaves it enabled, then that can break our use
- * of the bit-banging I2C interfaces. This is notably the case with the
- * Mac Mini in EFI mode.
- */
-void
-intel_i2c_reset_gmbus(struct drm_device *dev)
+static struct i2c_adapter *
+intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ static const int map_pin_to_reg[] = {
+ 0,
+ GPIOB,
+ GPIOA,
+ GPIOC,
+ GPIOD,
+ GPIOE,
+ 0,
+ GPIOF,
+ };
+ struct intel_gpio *gpio;
- if (HAS_PCH_SPLIT(dev)) {
- I915_WRITE(PCH_GMBUS0, 0);
- } else {
- I915_WRITE(GMBUS0, 0);
+ if (pin < 1 || pin > 7)
+ return NULL;
+
+ gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL);
+ if (gpio == NULL)
+ return NULL;
+
+ 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, I2C_NAME_SIZE, "GPIO%c", "?BACDEF?"[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;
+}
+
+static int
+intel_i2c_quirk_xfer(struct drm_i915_private *dev_priv,
+ struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct intel_gpio *gpio = container_of(adapter,
+ struct intel_gpio,
+ adapter);
+ int ret;
+
+ intel_i2c_reset(dev_priv->dev);
+
+ intel_i2c_quirk_set(dev_priv, true);
+ set_data(gpio, 1);
+ set_clock(gpio, 1);
+ udelay(I2C_RISEFALL_TIME);
+
+ ret = adapter->algo->master_xfer(adapter, msgs, num);
+
+ set_data(gpio, 1);
+ set_clock(gpio, 1);
+ intel_i2c_quirk_set(dev_priv, false);
+
+ return ret;
+}
+
+static int
+gmbus_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct intel_gmbus *bus = container_of(adapter,
+ struct intel_gmbus,
+ adapter);
+ struct drm_i915_private *dev_priv = adapter->algo_data;
+ int i, reg_offset;
+
+ if (bus->force_bit)
+ return intel_i2c_quirk_xfer(dev_priv,
+ bus->force_bit, msgs, num);
+
+ reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0;
+
+ I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
+
+ for (i = 0; i < num; i++) {
+ u16 len = msgs[i].len;
+ u8 *buf = msgs[i].buf;
+
+ if (msgs[i].flags & I2C_M_RD) {
+ I915_WRITE(GMBUS1 + reg_offset,
+ 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);
+ POSTING_READ(GMBUS2+reg_offset);
+ do {
+ u32 val, loop = 0;
+
+ if (wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50))
+ goto timeout;
+ if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
+ return 0;
+
+ val = I915_READ(GMBUS3 + reg_offset);
+ do {
+ *buf++ = val & 0xff;
+ val >>= 8;
+ } while (--len && ++loop < 4);
+ } while (len);
+ } else {
+ u32 val, loop;
+
+ val = loop = 0;
+ do {
+ val |= *buf++ << (8 * loop);
+ } while (--len && ++loop < 4);
+
+ I915_WRITE(GMBUS3 + reg_offset, val);
+ I915_WRITE(GMBUS1 + reg_offset,
+ (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) |
+ (msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) |
+ (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
+ GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
+ POSTING_READ(GMBUS2+reg_offset);
+
+ while (len) {
+ if (wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50))
+ goto timeout;
+ if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
+ return 0;
+
+ val = loop = 0;
+ do {
+ val |= *buf++ << (8 * loop);
+ } while (--len && ++loop < 4);
+
+ I915_WRITE(GMBUS3 + reg_offset, val);
+ POSTING_READ(GMBUS2+reg_offset);
+ }
+ }
+
+ if (i + 1 < num && wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50))
+ goto timeout;
+ if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
+ return 0;
}
+
+ return num;
+
+timeout:
+ DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n",
+ bus->reg0 & 0xff, bus->adapter.name);
+ /* 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);
}
+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 |
+ /* I2C_FUNC_10BIT_ADDR | */
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL);
+}
+
+static const struct i2c_algorithm gmbus_algorithm = {
+ .master_xfer = gmbus_xfer,
+ .functionality = gmbus_func
+};
+
/**
- * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * intel_gmbus_setup - instantiate all Intel i2c GMBuses
* @dev: DRM device
- * @output: driver specific output device
- * @reg: GPIO reg to use
- * @name: name for this bus
- * @slave_addr: slave address (if fixed)
- *
- * Creates and registers a new i2c bus with the Linux i2c layer, for use
- * in output probing and control (e.g. DDC or SDVO control functions).
- *
- * Possible values for @reg include:
- * %GPIOA
- * %GPIOB
- * %GPIOC
- * %GPIOD
- * %GPIOE
- * %GPIOF
- * %GPIOG
- * %GPIOH
- * see PRM for details on how these different busses are used.
*/
-struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg,
- const char *name)
+int intel_setup_gmbus(struct drm_device *dev)
{
- struct intel_i2c_chan *chan;
+ static const char *names[GMBUS_NUM_PORTS] = {
+ "disabled",
+ "ssc",
+ "vga",
+ "panel",
+ "dpc",
+ "dpb",
+ "reserved"
+ "dpd",
+ };
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret, i;
- chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL);
- if (!chan)
- goto out_free;
+ dev_priv->gmbus = kcalloc(sizeof(struct intel_gmbus), GMBUS_NUM_PORTS,
+ GFP_KERNEL);
+ if (dev_priv->gmbus == NULL)
+ return -ENOMEM;
- chan->drm_dev = dev;
- chan->reg = reg;
- snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
- chan->adapter.owner = THIS_MODULE;
- chan->adapter.algo_data = &chan->algo;
- chan->adapter.dev.parent = &dev->pdev->dev;
- chan->algo.setsda = set_data;
- chan->algo.setscl = set_clock;
- chan->algo.getsda = get_data;
- chan->algo.getscl = get_clock;
- chan->algo.udelay = 20;
- chan->algo.timeout = usecs_to_jiffies(2200);
- chan->algo.data = chan;
-
- i2c_set_adapdata(&chan->adapter, chan);
-
- if(i2c_bit_add_bus(&chan->adapter))
- goto out_free;
+ for (i = 0; i < GMBUS_NUM_PORTS; i++) {
+ struct intel_gmbus *bus = &dev_priv->gmbus[i];
- intel_i2c_reset_gmbus(dev);
+ bus->adapter.owner = THIS_MODULE;
+ bus->adapter.class = I2C_CLASS_DDC;
+ snprintf(bus->adapter.name,
+ I2C_NAME_SIZE,
+ "gmbus %s",
+ names[i]);
- /* JJJ: raise SCL and SDA? */
- intel_i2c_quirk_set(dev, true);
- set_data(chan, 1);
- set_clock(chan, 1);
- intel_i2c_quirk_set(dev, false);
- udelay(20);
+ bus->adapter.dev.parent = &dev->pdev->dev;
+ bus->adapter.algo_data = dev_priv;
- return &chan->adapter;
+ bus->adapter.algo = &gmbus_algorithm;
+ ret = i2c_add_adapter(&bus->adapter);
+ if (ret)
+ goto err;
-out_free:
- kfree(chan);
- return NULL;
+ /* By default use a conservative clock rate */
+ bus->reg0 = i | GMBUS_RATE_100KHZ;
+
+ /* XXX force bit banging until GMBUS is fully debugged */
+ bus->force_bit = intel_gpio_create(dev_priv, i);
+ }
+
+ intel_i2c_reset(dev_priv->dev);
+
+ return 0;
+
+err:
+ while (--i) {
+ struct intel_gmbus *bus = &dev_priv->gmbus[i];
+ i2c_del_adapter(&bus->adapter);
+ }
+ kfree(dev_priv->gmbus);
+ dev_priv->gmbus = NULL;
+ return ret;
}
-/**
- * intel_i2c_destroy - unregister and free i2c bus resources
- * @output: channel to free
- *
- * Unregister the adapter from the i2c layer, then free the structure.
- */
-void intel_i2c_destroy(struct i2c_adapter *adapter)
+void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed)
+{
+ struct intel_gmbus *bus = to_intel_gmbus(adapter);
+
+ /* speed:
+ * 0x0 = 100 KHz
+ * 0x1 = 50 KHz
+ * 0x2 = 400 KHz
+ * 0x3 = 1000 Khz
+ */
+ bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 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;
+ }
+ }
+}
+
+void intel_teardown_gmbus(struct drm_device *dev)
{
- struct intel_i2c_chan *chan;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int i;
- if (!adapter)
+ if (dev_priv->gmbus == NULL)
return;
- chan = container_of(adapter,
- struct intel_i2c_chan,
- adapter);
- i2c_del_adapter(&chan->adapter);
- kfree(chan);
+ 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);
+ }
+
+ kfree(dev_priv->gmbus);
+ dev_priv->gmbus = NULL;
}
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 6ec39a86ed06..f1a649990ea9 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -43,102 +43,76 @@
/* Private structure for the integrated LVDS support */
struct intel_lvds {
struct intel_encoder base;
+
+ struct edid *edid;
+
int fitting_mode;
u32 pfit_control;
u32 pfit_pgm_ratios;
+ bool pfit_dirty;
+
+ struct drm_display_mode *fixed_mode;
};
-static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder)
+static struct intel_lvds *to_intel_lvds(struct drm_encoder *encoder)
{
- return container_of(enc_to_intel_encoder(encoder), struct intel_lvds, base);
-}
-
-/**
- * Sets the backlight level.
- *
- * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
- */
-static void intel_lvds_set_backlight(struct drm_device *dev, int level)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 blc_pwm_ctl, reg;
-
- if (HAS_PCH_SPLIT(dev))
- reg = BLC_PWM_CPU_CTL;
- else
- reg = BLC_PWM_CTL;
-
- blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK;
- I915_WRITE(reg, (blc_pwm_ctl |
- (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
+ return container_of(encoder, struct intel_lvds, base.base);
}
-/**
- * Returns the maximum level of the backlight duty cycle field.
- */
-static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
+static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 reg;
-
- if (HAS_PCH_SPLIT(dev))
- reg = BLC_PWM_PCH_CTL2;
- else
- reg = BLC_PWM_CTL;
-
- return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >>
- BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+ return container_of(intel_attached_encoder(connector),
+ struct intel_lvds, base);
}
/**
* Sets the power state for the panel.
*/
-static void intel_lvds_set_power(struct drm_device *dev, bool on)
+static void intel_lvds_set_power(struct intel_lvds *intel_lvds, bool on)
{
+ struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 ctl_reg, status_reg, lvds_reg;
+ u32 ctl_reg, lvds_reg;
if (HAS_PCH_SPLIT(dev)) {
ctl_reg = PCH_PP_CONTROL;
- status_reg = PCH_PP_STATUS;
lvds_reg = PCH_LVDS;
} else {
ctl_reg = PP_CONTROL;
- status_reg = PP_STATUS;
lvds_reg = LVDS;
}
if (on) {
I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
- POSTING_READ(lvds_reg);
-
- I915_WRITE(ctl_reg, I915_READ(ctl_reg) |
- POWER_TARGET_ON);
- if (wait_for(I915_READ(status_reg) & PP_ON, 1000, 0))
- DRM_ERROR("timed out waiting to enable LVDS pipe");
-
- intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
+ I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
+ intel_panel_set_backlight(dev, dev_priv->backlight_level);
} else {
- intel_lvds_set_backlight(dev, 0);
+ dev_priv->backlight_level = intel_panel_get_backlight(dev);
+
+ intel_panel_set_backlight(dev, 0);
+ I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
- I915_WRITE(ctl_reg, I915_READ(ctl_reg) &
- ~POWER_TARGET_ON);
- if (wait_for((I915_READ(status_reg) & PP_ON) == 0, 1000, 0))
- DRM_ERROR("timed out waiting for LVDS pipe to turn off");
+ if (intel_lvds->pfit_control) {
+ if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000))
+ DRM_ERROR("timed out waiting for panel to power off\n");
+ I915_WRITE(PFIT_CONTROL, 0);
+ intel_lvds->pfit_control = 0;
+ intel_lvds->pfit_dirty = false;
+ }
I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN);
- POSTING_READ(lvds_reg);
}
+ POSTING_READ(lvds_reg);
}
static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
{
- struct drm_device *dev = encoder->dev;
+ struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
if (mode == DRM_MODE_DPMS_ON)
- intel_lvds_set_power(dev, true);
+ intel_lvds_set_power(intel_lvds, true);
else
- intel_lvds_set_power(dev, false);
+ intel_lvds_set_power(intel_lvds, false);
/* XXX: We never power down the LVDS pairs. */
}
@@ -146,16 +120,13 @@ static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
static int intel_lvds_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode;
+ struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
+ struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode;
- if (fixed_mode) {
- if (mode->hdisplay > fixed_mode->hdisplay)
- return MODE_PANEL;
- if (mode->vdisplay > fixed_mode->vdisplay)
- return MODE_PANEL;
- }
+ if (mode->hdisplay > fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > fixed_mode->vdisplay)
+ return MODE_PANEL;
return MODE_OK;
}
@@ -223,12 +194,12 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
- struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder);
+ struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
struct drm_encoder *tmp_encoder;
u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
/* Should never happen!! */
- if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
+ if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) {
DRM_ERROR("Can't support LVDS on pipe A\n");
return false;
}
@@ -241,9 +212,6 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
return false;
}
}
- /* If we don't have a panel mode, there is nothing we can do */
- if (dev_priv->panel_fixed_mode == NULL)
- return true;
/*
* We have timings from the BIOS for the panel, put them in
@@ -251,7 +219,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
* with the panel scaling set up to source from the H/VDisplay
* of the original mode.
*/
- intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode);
+ intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode);
if (HAS_PCH_SPLIT(dev)) {
intel_pch_panel_fitting(dev, intel_lvds->fitting_mode,
@@ -260,8 +228,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
}
/* Make sure pre-965s set dither correctly */
- if (!IS_I965G(dev)) {
- if (dev_priv->panel_wants_dither || dev_priv->lvds_dither)
+ if (INTEL_INFO(dev)->gen < 4) {
+ if (dev_priv->lvds_dither)
pfit_control |= PANEL_8TO6_DITHER_ENABLE;
}
@@ -271,7 +239,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
goto out;
/* 965+ wants fuzzy fitting */
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
PFIT_FILTER_FUZZY);
@@ -297,7 +265,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
case DRM_MODE_SCALE_ASPECT:
/* Scale but preserve the aspect ratio */
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
@@ -356,7 +324,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
* Fortunately this is all done for us in hw.
*/
pfit_control |= PFIT_ENABLE;
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
pfit_control |= PFIT_SCALING_AUTO;
else
pfit_control |= (VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
@@ -369,8 +337,12 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
}
out:
- intel_lvds->pfit_control = pfit_control;
- intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios;
+ if (pfit_control != intel_lvds->pfit_control ||
+ pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) {
+ intel_lvds->pfit_control = pfit_control;
+ intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios;
+ intel_lvds->pfit_dirty = true;
+ }
dev_priv->lvds_border_bits = border;
/*
@@ -386,30 +358,60 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 reg;
-
- if (HAS_PCH_SPLIT(dev))
- reg = BLC_PWM_CPU_CTL;
- else
- reg = BLC_PWM_CTL;
-
- dev_priv->saveBLC_PWM_CTL = I915_READ(reg);
- dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
- BACKLIGHT_DUTY_CYCLE_MASK);
+ struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
+
+ dev_priv->backlight_level = intel_panel_get_backlight(dev);
+
+ /* We try to do the minimum that is necessary in order to unlock
+ * the registers for mode setting.
+ *
+ * On Ironlake, this is quite simple as we just set the unlock key
+ * and ignore all subtleties. (This may cause some issues...)
+ *
+ * Prior to Ironlake, we must disable the pipe if we want to adjust
+ * the panel fitter. However at all other times we can just reset
+ * the registers regardless.
+ */
- intel_lvds_set_power(dev, false);
+ if (HAS_PCH_SPLIT(dev)) {
+ I915_WRITE(PCH_PP_CONTROL,
+ I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
+ } else if (intel_lvds->pfit_dirty) {
+ I915_WRITE(PP_CONTROL,
+ (I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS)
+ & ~POWER_TARGET_ON);
+ } else {
+ I915_WRITE(PP_CONTROL,
+ I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
+ }
}
-static void intel_lvds_commit( struct drm_encoder *encoder)
+static void intel_lvds_commit(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
- if (dev_priv->backlight_duty_cycle == 0)
- dev_priv->backlight_duty_cycle =
- intel_lvds_get_max_backlight(dev);
+ if (dev_priv->backlight_level == 0)
+ dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
+
+ /* Undo any unlocking done in prepare to prevent accidental
+ * adjustment of the registers.
+ */
+ if (HAS_PCH_SPLIT(dev)) {
+ u32 val = I915_READ(PCH_PP_CONTROL);
+ if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)
+ I915_WRITE(PCH_PP_CONTROL, val & 0x3);
+ } else {
+ u32 val = I915_READ(PP_CONTROL);
+ if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)
+ I915_WRITE(PP_CONTROL, val & 0x3);
+ }
- intel_lvds_set_power(dev, true);
+ /* Always do a full power on as we do not know what state
+ * we were left in.
+ */
+ intel_lvds_set_power(intel_lvds, true);
}
static void intel_lvds_mode_set(struct drm_encoder *encoder,
@@ -418,7 +420,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder);
+ struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
/*
* The LVDS pin pair will already have been turned on in the
@@ -429,13 +431,23 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
if (HAS_PCH_SPLIT(dev))
return;
+ if (!intel_lvds->pfit_dirty)
+ return;
+
/*
* Enable automatic panel scaling so that non-native modes fill the
* screen. Should be enabled before the pipe is enabled, according to
* register description and PRM.
*/
+ DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
+ intel_lvds->pfit_control,
+ intel_lvds->pfit_pgm_ratios);
+ if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000))
+ DRM_ERROR("timed out waiting for panel to power off\n");
+
I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
+ intel_lvds->pfit_dirty = false;
}
/**
@@ -465,38 +477,22 @@ intel_lvds_detect(struct drm_connector *connector, bool force)
*/
static int intel_lvds_get_modes(struct drm_connector *connector)
{
+ struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct drm_device *dev = connector->dev;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
- struct drm_i915_private *dev_priv = dev->dev_private;
- int ret = 0;
-
- if (dev_priv->lvds_edid_good) {
- ret = intel_ddc_get_modes(connector, intel_encoder->ddc_bus);
+ struct drm_display_mode *mode;
- if (ret)
- return ret;
+ if (intel_lvds->edid) {
+ drm_mode_connector_update_edid_property(connector,
+ intel_lvds->edid);
+ return drm_add_edid_modes(connector, intel_lvds->edid);
}
- /* Didn't get an EDID, so
- * Set wide sync ranges so we get all modes
- * handed to valid_mode for checking
- */
- 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 (dev_priv->panel_fixed_mode != NULL) {
- struct drm_display_mode *mode;
-
- mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
- drm_mode_probed_add(connector, mode);
-
- return 1;
- }
+ mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode);
+ if (mode == 0)
+ return 0;
- return 0;
+ drm_mode_probed_add(connector, mode);
+ return 1;
}
static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id)
@@ -587,18 +583,17 @@ static int intel_lvds_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
+ struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct drm_device *dev = connector->dev;
- if (property == dev->mode_config.scaling_mode_property &&
- connector->encoder) {
- struct drm_crtc *crtc = connector->encoder->crtc;
- struct drm_encoder *encoder = connector->encoder;
- struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder);
+ if (property == dev->mode_config.scaling_mode_property) {
+ struct drm_crtc *crtc = intel_lvds->base.base.crtc;
if (value == DRM_MODE_SCALE_NONE) {
DRM_DEBUG_KMS("no scaling not supported\n");
- return 0;
+ return -EINVAL;
}
+
if (intel_lvds->fitting_mode == value) {
/* the LVDS scaling property is not changed */
return 0;
@@ -628,7 +623,7 @@ static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
.get_modes = intel_lvds_get_modes,
.mode_valid = intel_lvds_mode_valid,
- .best_encoder = intel_attached_encoder,
+ .best_encoder = intel_best_encoder,
};
static const struct drm_connector_funcs intel_lvds_connector_funcs = {
@@ -726,16 +721,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
* Find the reduced downclock for LVDS in EDID.
*/
static void intel_find_lvds_downclock(struct drm_device *dev,
- struct drm_connector *connector)
+ struct drm_display_mode *fixed_mode,
+ struct drm_connector *connector)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_display_mode *scan, *panel_fixed_mode;
+ struct drm_display_mode *scan;
int temp_downclock;
- panel_fixed_mode = dev_priv->panel_fixed_mode;
- temp_downclock = panel_fixed_mode->clock;
-
- mutex_lock(&dev->mode_config.mutex);
+ temp_downclock = fixed_mode->clock;
list_for_each_entry(scan, &connector->probed_modes, head) {
/*
* If one mode has the same resolution with the fixed_panel
@@ -744,14 +737,14 @@ static void intel_find_lvds_downclock(struct drm_device *dev,
* case we can set the different FPx0/1 to dynamically select
* between low and high frequency.
*/
- if (scan->hdisplay == panel_fixed_mode->hdisplay &&
- scan->hsync_start == panel_fixed_mode->hsync_start &&
- scan->hsync_end == panel_fixed_mode->hsync_end &&
- scan->htotal == panel_fixed_mode->htotal &&
- scan->vdisplay == panel_fixed_mode->vdisplay &&
- scan->vsync_start == panel_fixed_mode->vsync_start &&
- scan->vsync_end == panel_fixed_mode->vsync_end &&
- scan->vtotal == panel_fixed_mode->vtotal) {
+ if (scan->hdisplay == fixed_mode->hdisplay &&
+ scan->hsync_start == fixed_mode->hsync_start &&
+ scan->hsync_end == fixed_mode->hsync_end &&
+ scan->htotal == fixed_mode->htotal &&
+ scan->vdisplay == fixed_mode->vdisplay &&
+ scan->vsync_start == fixed_mode->vsync_start &&
+ scan->vsync_end == fixed_mode->vsync_end &&
+ scan->vtotal == fixed_mode->vtotal) {
if (scan->clock < temp_downclock) {
/*
* The downclock is already found. But we
@@ -761,17 +754,14 @@ static void intel_find_lvds_downclock(struct drm_device *dev,
}
}
}
- mutex_unlock(&dev->mode_config.mutex);
- if (temp_downclock < panel_fixed_mode->clock &&
- i915_lvds_downclock) {
+ if (temp_downclock < fixed_mode->clock && i915_lvds_downclock) {
/* We found the downclock for LVDS. */
dev_priv->lvds_downclock_avail = 1;
dev_priv->lvds_downclock = temp_downclock;
DRM_DEBUG_KMS("LVDS downclock is found in EDID. "
- "Normal clock %dKhz, downclock %dKhz\n",
- panel_fixed_mode->clock, temp_downclock);
+ "Normal clock %dKhz, downclock %dKhz\n",
+ fixed_mode->clock, temp_downclock);
}
- return;
}
/*
@@ -780,38 +770,67 @@ static void intel_find_lvds_downclock(struct drm_device *dev,
* If it is present, return 1.
* If it is not present, return false.
* If no child dev is parsed from VBT, it assumes that the LVDS is present.
- * Note: The addin_offset should also be checked for LVDS panel.
- * Only when it is non-zero, it is assumed that it is present.
*/
-static int lvds_is_present_in_vbt(struct drm_device *dev)
+static bool lvds_is_present_in_vbt(struct drm_device *dev,
+ u8 *i2c_pin)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct child_device_config *p_child;
- int i, ret;
+ int i;
if (!dev_priv->child_dev_num)
- return 1;
+ return true;
- ret = 0;
for (i = 0; i < dev_priv->child_dev_num; i++) {
- p_child = dev_priv->child_dev + i;
- /*
- * If the device type is not LFP, continue.
- * If the device type is 0x22, it is also regarded as LFP.
+ struct child_device_config *child = dev_priv->child_dev + i;
+
+ /* If the device type is not LFP, continue.
+ * We have to check both the new identifiers as well as the
+ * old for compatibility with some BIOSes.
*/
- if (p_child->device_type != DEVICE_TYPE_INT_LFP &&
- p_child->device_type != DEVICE_TYPE_LFP)
+ if (child->device_type != DEVICE_TYPE_INT_LFP &&
+ child->device_type != DEVICE_TYPE_LFP)
continue;
- /* The addin_offset should be checked. Only when it is
- * non-zero, it is regarded as present.
+ if (child->i2c_pin)
+ *i2c_pin = child->i2c_pin;
+
+ /* However, we cannot trust the BIOS writers to populate
+ * the VBT correctly. Since LVDS requires additional
+ * information from AIM blocks, a non-zero addin offset is
+ * a good indicator that the LVDS is actually present.
*/
- if (p_child->addin_offset) {
- ret = 1;
- break;
- }
+ if (child->addin_offset)
+ return true;
+
+ /* But even then some BIOS writers perform some black magic
+ * and instantiate the device without reference to any
+ * additional data. Trust that if the VBT was written into
+ * the OpRegion then they have validated the LVDS's existence.
+ */
+ if (dev_priv->opregion.vbt)
+ return true;
}
- return ret;
+
+ return false;
+}
+
+static bool intel_lvds_ddc_probe(struct drm_device *dev, u8 pin)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u8 buf = 0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = 0xA0,
+ .flags = 0,
+ .len = 1,
+ .buf = &buf,
+ },
+ };
+ struct i2c_adapter *i2c = &dev_priv->gmbus[pin].adapter;
+ /* XXX this only appears to work when using GMBUS */
+ if (intel_gmbus_is_forced_bit(i2c))
+ return true;
+ return i2c_transfer(i2c, msgs, 1) == 1;
}
/**
@@ -832,13 +851,15 @@ void intel_lvds_init(struct drm_device *dev)
struct drm_display_mode *scan; /* *modes, *bios_mode; */
struct drm_crtc *crtc;
u32 lvds;
- int pipe, gpio = GPIOC;
+ int pipe;
+ u8 pin;
/* Skip init on machines we know falsely report LVDS */
if (dmi_check_system(intel_no_lvds))
return;
- if (!lvds_is_present_in_vbt(dev)) {
+ pin = GMBUS_PORT_PANEL;
+ if (!lvds_is_present_in_vbt(dev, &pin)) {
DRM_DEBUG_KMS("LVDS is not present in VBT\n");
return;
}
@@ -846,11 +867,15 @@ void intel_lvds_init(struct drm_device *dev)
if (HAS_PCH_SPLIT(dev)) {
if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
return;
- if (dev_priv->edp_support) {
+ if (dev_priv->edp.support) {
DRM_DEBUG_KMS("disable LVDS for eDP support\n");
return;
}
- gpio = PCH_GPIOC;
+ }
+
+ if (!intel_lvds_ddc_probe(dev, pin)) {
+ DRM_DEBUG_KMS("LVDS did not respond to DDC probe\n");
+ return;
}
intel_lvds = kzalloc(sizeof(struct intel_lvds), GFP_KERNEL);
@@ -864,16 +889,20 @@ void intel_lvds_init(struct drm_device *dev)
return;
}
+ if (!HAS_PCH_SPLIT(dev)) {
+ intel_lvds->pfit_control = I915_READ(PFIT_CONTROL);
+ }
+
intel_encoder = &intel_lvds->base;
- encoder = &intel_encoder->enc;
+ encoder = &intel_encoder->base;
connector = &intel_connector->base;
drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
- drm_encoder_init(dev, &intel_encoder->enc, &intel_lvds_enc_funcs,
+ drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
DRM_MODE_ENCODER_LVDS);
- drm_mode_connector_attach_encoder(&intel_connector->base, &intel_encoder->enc);
+ intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_LVDS;
intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
@@ -904,43 +933,41 @@ void intel_lvds_init(struct drm_device *dev)
* if closed, act like it's not there for now
*/
- /* Set up the DDC bus. */
- intel_encoder->ddc_bus = intel_i2c_create(dev, gpio, "LVDSDDC_C");
- if (!intel_encoder->ddc_bus) {
- dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
- "failed.\n");
- goto failed;
- }
-
/*
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
- dev_priv->lvds_edid_good = true;
+ intel_lvds->edid = drm_get_edid(connector,
+ &dev_priv->gmbus[pin].adapter);
- if (!intel_ddc_get_modes(connector, intel_encoder->ddc_bus))
- dev_priv->lvds_edid_good = false;
+ if (!intel_lvds->edid) {
+ /* Didn't get an EDID, so
+ * Set wide sync ranges so we get all modes
+ * handed to valid_mode for checking
+ */
+ connector->display_info.min_vfreq = 0;
+ connector->display_info.max_vfreq = 200;
+ connector->display_info.min_hfreq = 0;
+ connector->display_info.max_hfreq = 200;
+ }
list_for_each_entry(scan, &connector->probed_modes, head) {
- mutex_lock(&dev->mode_config.mutex);
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
- dev_priv->panel_fixed_mode =
+ intel_lvds->fixed_mode =
drm_mode_duplicate(dev, scan);
- mutex_unlock(&dev->mode_config.mutex);
- intel_find_lvds_downclock(dev, connector);
+ intel_find_lvds_downclock(dev,
+ intel_lvds->fixed_mode,
+ connector);
goto out;
}
- mutex_unlock(&dev->mode_config.mutex);
}
/* Failed to get EDID, what about VBT? */
if (dev_priv->lfp_lvds_vbt_mode) {
- mutex_lock(&dev->mode_config.mutex);
- dev_priv->panel_fixed_mode =
+ intel_lvds->fixed_mode =
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
- mutex_unlock(&dev->mode_config.mutex);
- if (dev_priv->panel_fixed_mode) {
- dev_priv->panel_fixed_mode->type |=
+ if (intel_lvds->fixed_mode) {
+ intel_lvds->fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
goto out;
}
@@ -958,19 +985,19 @@ void intel_lvds_init(struct drm_device *dev)
lvds = I915_READ(LVDS);
pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
- crtc = intel_get_crtc_from_pipe(dev, pipe);
+ crtc = intel_get_crtc_for_pipe(dev, pipe);
if (crtc && (lvds & LVDS_PORT_EN)) {
- dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc);
- if (dev_priv->panel_fixed_mode) {
- dev_priv->panel_fixed_mode->type |=
+ intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc);
+ if (intel_lvds->fixed_mode) {
+ intel_lvds->fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
goto out;
}
}
/* If we still don't have a mode after all that, give up. */
- if (!dev_priv->panel_fixed_mode)
+ if (!intel_lvds->fixed_mode)
goto failed;
out:
@@ -997,8 +1024,6 @@ out:
failed:
DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
- if (intel_encoder->ddc_bus)
- intel_i2c_destroy(intel_encoder->ddc_bus);
drm_connector_cleanup(connector);
drm_encoder_cleanup(encoder);
kfree(intel_lvds);
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 4b1fd3d9c73c..f70b7cf32bff 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
- * Copyright (c) 2007 Intel Corporation
+ * Copyright (c) 2007, 2010 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -34,11 +34,11 @@
* intel_ddc_probe
*
*/
-bool intel_ddc_probe(struct intel_encoder *intel_encoder)
+bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus)
{
+ struct drm_i915_private *dev_priv = intel_encoder->base.dev->dev_private;
u8 out_buf[] = { 0x0, 0x0};
u8 buf[2];
- int ret;
struct i2c_msg msgs[] = {
{
.addr = 0x50,
@@ -54,13 +54,7 @@ bool intel_ddc_probe(struct intel_encoder *intel_encoder)
}
};
- intel_i2c_quirk_set(intel_encoder->enc.dev, true);
- ret = i2c_transfer(intel_encoder->ddc_bus, msgs, 2);
- intel_i2c_quirk_set(intel_encoder->enc.dev, false);
- if (ret == 2)
- return true;
-
- return false;
+ return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 2) == 2;
}
/**
@@ -76,9 +70,7 @@ int intel_ddc_get_modes(struct drm_connector *connector,
struct edid *edid;
int ret = 0;
- intel_i2c_quirk_set(connector->dev, true);
edid = drm_get_edid(connector, adapter);
- intel_i2c_quirk_set(connector->dev, false);
if (edid) {
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index ea5d3fea4b61..917c7dc3cd6b 100644
--- a/drivers/gpu/drm/i915/i915_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -31,17 +31,16 @@
#include "drmP.h"
#include "i915_drm.h"
#include "i915_drv.h"
+#include "intel_drv.h"
#define PCI_ASLE 0xe4
-#define PCI_LBPC 0xf4
#define PCI_ASLS 0xfc
-#define OPREGION_SZ (8*1024)
#define OPREGION_HEADER_OFFSET 0
#define OPREGION_ACPI_OFFSET 0x100
#define OPREGION_SWSCI_OFFSET 0x200
#define OPREGION_ASLE_OFFSET 0x300
-#define OPREGION_VBT_OFFSET 0x1000
+#define OPREGION_VBT_OFFSET 0x400
#define OPREGION_SIGNATURE "IntelGraphicsMem"
#define MBOX_ACPI (1<<0)
@@ -143,40 +142,22 @@ struct opregion_asle {
#define ACPI_DIGITAL_OUTPUT (3<<8)
#define ACPI_LVDS_OUTPUT (4<<8)
+#ifdef CONFIG_ACPI
static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
- u32 blc_pwm_ctl, blc_pwm_ctl2;
- u32 max_backlight, level, shift;
+ u32 max;
if (!(bclp & ASLE_BCLP_VALID))
return ASLE_BACKLIGHT_FAILED;
bclp &= ASLE_BCLP_MSK;
- if (bclp < 0 || bclp > 255)
+ if (bclp > 255)
return ASLE_BACKLIGHT_FAILED;
- blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
- blc_pwm_ctl2 = I915_READ(BLC_PWM_CTL2);
-
- if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE))
- pci_write_config_dword(dev->pdev, PCI_LBPC, bclp);
- else {
- if (IS_PINEVIEW(dev)) {
- blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
- max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >>
- BACKLIGHT_MODULATION_FREQ_SHIFT;
- shift = BACKLIGHT_DUTY_CYCLE_SHIFT + 1;
- } else {
- blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK;
- max_backlight = ((blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >>
- BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
- shift = BACKLIGHT_DUTY_CYCLE_SHIFT;
- }
- level = (bclp * max_backlight) / 255;
- I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | (level << shift));
- }
+ max = intel_panel_get_max_backlight(dev);
+ intel_panel_set_backlight(dev, bclp * max / 255);
asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
return 0;
@@ -211,7 +192,7 @@ static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
return 0;
}
-void opregion_asle_intr(struct drm_device *dev)
+void intel_opregion_asle_intr(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
@@ -243,37 +224,8 @@ void opregion_asle_intr(struct drm_device *dev)
asle->aslc = asle_stat;
}
-static u32 asle_set_backlight_ironlake(struct drm_device *dev, u32 bclp)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct opregion_asle *asle = dev_priv->opregion.asle;
- u32 cpu_pwm_ctl, pch_pwm_ctl2;
- u32 max_backlight, level;
-
- if (!(bclp & ASLE_BCLP_VALID))
- return ASLE_BACKLIGHT_FAILED;
-
- bclp &= ASLE_BCLP_MSK;
- if (bclp < 0 || bclp > 255)
- return ASLE_BACKLIGHT_FAILED;
-
- cpu_pwm_ctl = I915_READ(BLC_PWM_CPU_CTL);
- pch_pwm_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
- /* get the max PWM frequency */
- max_backlight = (pch_pwm_ctl2 >> 16) & BACKLIGHT_DUTY_CYCLE_MASK;
- /* calculate the expected PMW frequency */
- level = (bclp * max_backlight) / 255;
- /* reserve the high 16 bits */
- cpu_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK);
- /* write the updated PWM frequency */
- I915_WRITE(BLC_PWM_CPU_CTL, cpu_pwm_ctl | level);
-
- asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
-
- return 0;
-}
-
-void ironlake_opregion_gse_intr(struct drm_device *dev)
+/* Only present on Ironlake+ */
+void intel_opregion_gse_intr(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
@@ -296,7 +248,7 @@ void ironlake_opregion_gse_intr(struct drm_device *dev)
}
if (asle_req & ASLE_SET_BACKLIGHT)
- asle_stat |= asle_set_backlight_ironlake(dev, asle->bclp);
+ asle_stat |= asle_set_backlight(dev, asle->bclp);
if (asle_req & ASLE_SET_PFIT) {
DRM_DEBUG_DRIVER("Pfit is not supported\n");
@@ -315,7 +267,7 @@ void ironlake_opregion_gse_intr(struct drm_device *dev)
#define ASLE_PFIT_EN (1<<2)
#define ASLE_PFMB_EN (1<<3)
-void opregion_enable_asle(struct drm_device *dev)
+void intel_opregion_enable_asle(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_asle *asle = dev_priv->opregion.asle;
@@ -464,7 +416,58 @@ blind_set:
goto end;
}
-int intel_opregion_init(struct drm_device *dev, int resume)
+void intel_opregion_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_opregion *opregion = &dev_priv->opregion;
+
+ if (!opregion->header)
+ return;
+
+ if (opregion->acpi) {
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ intel_didl_outputs(dev);
+
+ /* Notify BIOS we are ready to handle ACPI video ext notifs.
+ * Right now, all the events are handled by the ACPI video module.
+ * We don't actually need to do anything with them. */
+ opregion->acpi->csts = 0;
+ opregion->acpi->drdy = 1;
+
+ system_opregion = opregion;
+ register_acpi_notifier(&intel_opregion_notifier);
+ }
+
+ if (opregion->asle)
+ intel_opregion_enable_asle(dev);
+}
+
+void intel_opregion_fini(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_opregion *opregion = &dev_priv->opregion;
+
+ if (!opregion->header)
+ return;
+
+ if (opregion->acpi) {
+ opregion->acpi->drdy = 0;
+
+ system_opregion = NULL;
+ unregister_acpi_notifier(&intel_opregion_notifier);
+ }
+
+ /* just clear all opregion memory pointers now */
+ iounmap(opregion->header);
+ opregion->header = NULL;
+ opregion->acpi = NULL;
+ opregion->swsci = NULL;
+ opregion->asle = NULL;
+ opregion->vbt = NULL;
+}
+#endif
+
+int intel_opregion_setup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
@@ -479,29 +482,23 @@ int intel_opregion_init(struct drm_device *dev, int resume)
return -ENOTSUPP;
}
- base = ioremap(asls, OPREGION_SZ);
+ base = ioremap(asls, OPREGION_SIZE);
if (!base)
return -ENOMEM;
- opregion->header = base;
- if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
+ if (memcmp(base, OPREGION_SIGNATURE, 16)) {
DRM_DEBUG_DRIVER("opregion signature mismatch\n");
err = -EINVAL;
goto err_out;
}
+ opregion->header = base;
+ opregion->vbt = base + OPREGION_VBT_OFFSET;
mboxes = opregion->header->mboxes;
if (mboxes & MBOX_ACPI) {
DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
opregion->acpi = base + OPREGION_ACPI_OFFSET;
- if (drm_core_check_feature(dev, DRIVER_MODESET))
- intel_didl_outputs(dev);
- } else {
- DRM_DEBUG_DRIVER("Public ACPI methods not supported\n");
- err = -ENOTSUPP;
- goto err_out;
}
- opregion->enabled = 1;
if (mboxes & MBOX_SWSCI) {
DRM_DEBUG_DRIVER("SWSCI supported\n");
@@ -510,53 +507,11 @@ int intel_opregion_init(struct drm_device *dev, int resume)
if (mboxes & MBOX_ASLE) {
DRM_DEBUG_DRIVER("ASLE supported\n");
opregion->asle = base + OPREGION_ASLE_OFFSET;
- opregion_enable_asle(dev);
}
- if (!resume)
- acpi_video_register();
-
-
- /* Notify BIOS we are ready to handle ACPI video ext notifs.
- * Right now, all the events are handled by the ACPI video module.
- * We don't actually need to do anything with them. */
- opregion->acpi->csts = 0;
- opregion->acpi->drdy = 1;
-
- system_opregion = opregion;
- register_acpi_notifier(&intel_opregion_notifier);
-
return 0;
err_out:
iounmap(opregion->header);
- opregion->header = NULL;
- acpi_video_register();
return err;
}
-
-void intel_opregion_free(struct drm_device *dev, int suspend)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_opregion *opregion = &dev_priv->opregion;
-
- if (!opregion->enabled)
- return;
-
- if (!suspend)
- acpi_video_unregister();
-
- opregion->acpi->drdy = 0;
-
- system_opregion = NULL;
- unregister_acpi_notifier(&intel_opregion_notifier);
-
- /* just clear all opregion memory pointers now */
- iounmap(opregion->header);
- opregion->header = NULL;
- opregion->acpi = NULL;
- opregion->swsci = NULL;
- opregion->asle = NULL;
-
- opregion->enabled = 0;
-}
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index 1d306a458be6..afb96d25219a 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -170,57 +170,143 @@ struct overlay_registers {
u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
};
-/* overlay flip addr flag */
-#define OFC_UPDATE 0x1
-
-#define OVERLAY_NONPHYSICAL(dev) (IS_G33(dev) || IS_I965G(dev))
-#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev) && !IS_GEN6(dev))
-
+struct intel_overlay {
+ struct drm_device *dev;
+ struct intel_crtc *crtc;
+ struct drm_i915_gem_object *vid_bo;
+ struct drm_i915_gem_object *old_vid_bo;
+ int active;
+ int pfit_active;
+ u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
+ u32 color_key;
+ u32 brightness, contrast, saturation;
+ u32 old_xscale, old_yscale;
+ /* register access */
+ u32 flip_addr;
+ struct drm_i915_gem_object *reg_bo;
+ /* flip handling */
+ uint32_t last_flip_req;
+ void (*flip_tail)(struct intel_overlay *);
+};
-static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
+static struct overlay_registers *
+intel_overlay_map_regs(struct intel_overlay *overlay)
{
drm_i915_private_t *dev_priv = overlay->dev->dev_private;
struct overlay_registers *regs;
- /* no recursive mappings */
- BUG_ON(overlay->virt_addr);
+ if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
+ regs = overlay->reg_bo->phys_obj->handle->vaddr;
+ else
+ regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping,
+ overlay->reg_bo->gtt_offset);
- if (OVERLAY_NONPHYSICAL(overlay->dev)) {
- regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
- overlay->reg_bo->gtt_offset,
- KM_USER0);
+ return regs;
+}
- if (!regs) {
- DRM_ERROR("failed to map overlay regs in GTT\n");
- return NULL;
- }
- } else
- regs = overlay->reg_bo->phys_obj->handle->vaddr;
+static void intel_overlay_unmap_regs(struct intel_overlay *overlay,
+ struct overlay_registers *regs)
+{
+ if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev))
+ io_mapping_unmap(regs);
+}
+
+static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
+ struct drm_i915_gem_request *request,
+ bool interruptible,
+ void (*tail)(struct intel_overlay *))
+{
+ struct drm_device *dev = overlay->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int ret;
- return overlay->virt_addr = regs;
+ BUG_ON(overlay->last_flip_req);
+ overlay->last_flip_req =
+ i915_add_request(dev, NULL, request, &dev_priv->render_ring);
+ if (overlay->last_flip_req == 0)
+ return -ENOMEM;
+
+ overlay->flip_tail = tail;
+ ret = i915_do_wait_request(dev,
+ overlay->last_flip_req, true,
+ &dev_priv->render_ring);
+ if (ret)
+ return ret;
+
+ overlay->last_flip_req = 0;
+ return 0;
}
-static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay)
+/* Workaround for i830 bug where pipe a must be enable to change control regs */
+static int
+i830_activate_pipe_a(struct drm_device *dev)
{
- if (OVERLAY_NONPHYSICAL(overlay->dev))
- io_mapping_unmap_atomic(overlay->virt_addr, KM_USER0);
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *crtc;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ struct drm_display_mode vesa_640x480 = {
+ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
+ 752, 800, 0, 480, 489, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
+ }, *mode;
+
+ crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]);
+ if (crtc->dpms_mode == DRM_MODE_DPMS_ON)
+ return 0;
- overlay->virt_addr = NULL;
+ /* most i8xx have pipe a forced on, so don't trust dpms mode */
+ if (I915_READ(PIPEACONF) & PIPECONF_ENABLE)
+ return 0;
- return;
+ crtc_funcs = crtc->base.helper_private;
+ if (crtc_funcs->dpms == NULL)
+ return 0;
+
+ 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);
+ if(!drm_crtc_helper_set_mode(&crtc->base, mode,
+ crtc->base.x, crtc->base.y,
+ crtc->base.fb))
+ return 0;
+
+ crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON);
+ return 1;
+}
+
+static void
+i830_deactivate_pipe_a(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0];
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
}
/* overlay needs to be disable in OCMD reg */
static int intel_overlay_on(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
+ struct drm_i915_gem_request *request;
+ int pipe_a_quirk = 0;
int ret;
- drm_i915_private_t *dev_priv = dev->dev_private;
BUG_ON(overlay->active);
-
overlay->active = 1;
- overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP;
+
+ if (IS_I830(dev)) {
+ pipe_a_quirk = i830_activate_pipe_a(dev);
+ if (pipe_a_quirk < 0)
+ return pipe_a_quirk;
+ }
+
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (request == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
BEGIN_LP_RING(4);
OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON);
@@ -229,32 +315,30 @@ static int intel_overlay_on(struct intel_overlay *overlay)
OUT_RING(MI_NOOP);
ADVANCE_LP_RING();
- overlay->last_flip_req =
- i915_add_request(dev, NULL, 0, &dev_priv->render_ring);
- if (overlay->last_flip_req == 0)
- return -ENOMEM;
-
- ret = i915_do_wait_request(dev,
- overlay->last_flip_req, 1, &dev_priv->render_ring);
- if (ret != 0)
- return ret;
+ ret = intel_overlay_do_wait_request(overlay, request, true, NULL);
+out:
+ if (pipe_a_quirk)
+ i830_deactivate_pipe_a(dev);
- overlay->hw_wedged = 0;
- overlay->last_flip_req = 0;
- return 0;
+ return ret;
}
/* overlay needs to be enabled in OCMD reg */
-static void intel_overlay_continue(struct intel_overlay *overlay,
- bool load_polyphase_filter)
+static int intel_overlay_continue(struct intel_overlay *overlay,
+ bool load_polyphase_filter)
{
struct drm_device *dev = overlay->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_request *request;
u32 flip_addr = overlay->flip_addr;
u32 tmp;
BUG_ON(!overlay->active);
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (request == NULL)
+ return -ENOMEM;
+
if (load_polyphase_filter)
flip_addr |= OFC_UPDATE;
@@ -269,220 +353,132 @@ static void intel_overlay_continue(struct intel_overlay *overlay,
ADVANCE_LP_RING();
overlay->last_flip_req =
- i915_add_request(dev, NULL, 0, &dev_priv->render_ring);
+ i915_add_request(dev, NULL, request, &dev_priv->render_ring);
+ return 0;
}
-static int intel_overlay_wait_flip(struct intel_overlay *overlay)
+static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay)
{
- struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- int ret;
- u32 tmp;
-
- if (overlay->last_flip_req != 0) {
- ret = i915_do_wait_request(dev, overlay->last_flip_req,
- 1, &dev_priv->render_ring);
- if (ret == 0) {
- overlay->last_flip_req = 0;
-
- tmp = I915_READ(ISR);
+ struct drm_gem_object *obj = &overlay->old_vid_bo->base;
- if (!(tmp & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT))
- return 0;
- }
- }
+ i915_gem_object_unpin(obj);
+ drm_gem_object_unreference(obj);
- /* synchronous slowpath */
- overlay->hw_wedged = RELEASE_OLD_VID;
+ overlay->old_vid_bo = NULL;
+}
- BEGIN_LP_RING(2);
- OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
- OUT_RING(MI_NOOP);
- ADVANCE_LP_RING();
+static void intel_overlay_off_tail(struct intel_overlay *overlay)
+{
+ struct drm_gem_object *obj;
- overlay->last_flip_req =
- i915_add_request(dev, NULL, 0, &dev_priv->render_ring);
- if (overlay->last_flip_req == 0)
- return -ENOMEM;
+ /* never have the overlay hw on without showing a frame */
+ BUG_ON(!overlay->vid_bo);
+ obj = &overlay->vid_bo->base;
- ret = i915_do_wait_request(dev, overlay->last_flip_req,
- 1, &dev_priv->render_ring);
- if (ret != 0)
- return ret;
+ i915_gem_object_unpin(obj);
+ drm_gem_object_unreference(obj);
+ overlay->vid_bo = NULL;
- overlay->hw_wedged = 0;
- overlay->last_flip_req = 0;
- return 0;
+ overlay->crtc->overlay = NULL;
+ overlay->crtc = NULL;
+ overlay->active = 0;
}
/* overlay needs to be disabled in OCMD reg */
-static int intel_overlay_off(struct intel_overlay *overlay)
+static int intel_overlay_off(struct intel_overlay *overlay,
+ bool interruptible)
{
- u32 flip_addr = overlay->flip_addr;
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- int ret;
+ u32 flip_addr = overlay->flip_addr;
+ struct drm_i915_gem_request *request;
BUG_ON(!overlay->active);
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (request == NULL)
+ return -ENOMEM;
+
/* According to intel docs the overlay hw may hang (when switching
* off) without loading the filter coeffs. It is however unclear whether
* this applies to the disabling of the overlay or to the switching off
* of the hw. Do it in both cases */
flip_addr |= OFC_UPDATE;
+ BEGIN_LP_RING(6);
/* wait for overlay to go idle */
- overlay->hw_wedged = SWITCH_OFF_STAGE_1;
-
- BEGIN_LP_RING(4);
OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
OUT_RING(flip_addr);
- OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
- OUT_RING(MI_NOOP);
- ADVANCE_LP_RING();
-
- overlay->last_flip_req =
- i915_add_request(dev, NULL, 0, &dev_priv->render_ring);
- if (overlay->last_flip_req == 0)
- return -ENOMEM;
-
- ret = i915_do_wait_request(dev, overlay->last_flip_req,
- 1, &dev_priv->render_ring);
- if (ret != 0)
- return ret;
-
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
/* turn overlay off */
- overlay->hw_wedged = SWITCH_OFF_STAGE_2;
-
- BEGIN_LP_RING(4);
- OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
+ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
OUT_RING(flip_addr);
- OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
- OUT_RING(MI_NOOP);
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
ADVANCE_LP_RING();
- overlay->last_flip_req =
- i915_add_request(dev, NULL, 0, &dev_priv->render_ring);
- if (overlay->last_flip_req == 0)
- return -ENOMEM;
-
- ret = i915_do_wait_request(dev, overlay->last_flip_req,
- 1, &dev_priv->render_ring);
- if (ret != 0)
- return ret;
-
- overlay->hw_wedged = 0;
- overlay->last_flip_req = 0;
- return ret;
-}
-
-static void intel_overlay_off_tail(struct intel_overlay *overlay)
-{
- struct drm_gem_object *obj;
-
- /* never have the overlay hw on without showing a frame */
- BUG_ON(!overlay->vid_bo);
- obj = &overlay->vid_bo->base;
-
- i915_gem_object_unpin(obj);
- drm_gem_object_unreference(obj);
- overlay->vid_bo = NULL;
-
- overlay->crtc->overlay = NULL;
- overlay->crtc = NULL;
- overlay->active = 0;
+ return intel_overlay_do_wait_request(overlay, request, interruptible,
+ intel_overlay_off_tail);
}
/* recover from an interruption due to a signal
* We have to be careful not to repeat work forever an make forward progess. */
-int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
- int interruptible)
+static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
+ bool interruptible)
{
struct drm_device *dev = overlay->dev;
- struct drm_gem_object *obj;
drm_i915_private_t *dev_priv = dev->dev_private;
- u32 flip_addr;
int ret;
- if (overlay->hw_wedged == HW_WEDGED)
- return -EIO;
-
- if (overlay->last_flip_req == 0) {
- overlay->last_flip_req =
- i915_add_request(dev, NULL, 0, &dev_priv->render_ring);
- if (overlay->last_flip_req == 0)
- return -ENOMEM;
- }
+ if (overlay->last_flip_req == 0)
+ return 0;
ret = i915_do_wait_request(dev, overlay->last_flip_req,
- interruptible, &dev_priv->render_ring);
- if (ret != 0)
+ interruptible, &dev_priv->render_ring);
+ if (ret)
return ret;
- switch (overlay->hw_wedged) {
- case RELEASE_OLD_VID:
- obj = &overlay->old_vid_bo->base;
- i915_gem_object_unpin(obj);
- drm_gem_object_unreference(obj);
- overlay->old_vid_bo = NULL;
- break;
- case SWITCH_OFF_STAGE_1:
- flip_addr = overlay->flip_addr;
- flip_addr |= OFC_UPDATE;
-
- overlay->hw_wedged = SWITCH_OFF_STAGE_2;
-
- BEGIN_LP_RING(4);
- OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
- OUT_RING(flip_addr);
- OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
- OUT_RING(MI_NOOP);
- ADVANCE_LP_RING();
-
- overlay->last_flip_req = i915_add_request(dev, NULL,
- 0, &dev_priv->render_ring);
- if (overlay->last_flip_req == 0)
- return -ENOMEM;
-
- ret = i915_do_wait_request(dev, overlay->last_flip_req,
- interruptible, &dev_priv->render_ring);
- if (ret != 0)
- return ret;
-
- case SWITCH_OFF_STAGE_2:
- intel_overlay_off_tail(overlay);
- break;
- default:
- BUG_ON(overlay->hw_wedged != NEEDS_WAIT_FOR_FLIP);
- }
+ if (overlay->flip_tail)
+ overlay->flip_tail(overlay);
- overlay->hw_wedged = 0;
overlay->last_flip_req = 0;
return 0;
}
/* Wait for pending overlay flip and release old frame.
* Needs to be called before the overlay register are changed
- * via intel_overlay_(un)map_regs_atomic */
+ * via intel_overlay_(un)map_regs
+ */
static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
{
+ struct drm_device *dev = overlay->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
- struct drm_gem_object *obj;
- /* only wait if there is actually an old frame to release to
- * guarantee forward progress */
+ /* Only wait if there is actually an old frame to release to
+ * guarantee forward progress.
+ */
if (!overlay->old_vid_bo)
return 0;
- ret = intel_overlay_wait_flip(overlay);
- if (ret != 0)
- return ret;
+ if (I915_READ(ISR) & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT) {
+ struct drm_i915_gem_request *request;
- obj = &overlay->old_vid_bo->base;
- i915_gem_object_unpin(obj);
- drm_gem_object_unreference(obj);
- overlay->old_vid_bo = NULL;
+ /* synchronous slowpath */
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (request == NULL)
+ return -ENOMEM;
+ BEGIN_LP_RING(2);
+ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+ OUT_RING(MI_NOOP);
+ ADVANCE_LP_RING();
+
+ ret = intel_overlay_do_wait_request(overlay, request, true,
+ intel_overlay_release_old_vid_tail);
+ if (ret)
+ return ret;
+ }
+
+ intel_overlay_release_old_vid_tail(overlay);
return 0;
}
@@ -506,65 +502,65 @@ struct put_image_params {
static int packed_depth_bytes(u32 format)
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
- case I915_OVERLAY_YUV422:
- return 4;
- case I915_OVERLAY_YUV411:
- /* return 6; not implemented */
- default:
- return -EINVAL;
+ case I915_OVERLAY_YUV422:
+ return 4;
+ case I915_OVERLAY_YUV411:
+ /* return 6; not implemented */
+ default:
+ return -EINVAL;
}
}
static int packed_width_bytes(u32 format, short width)
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
- case I915_OVERLAY_YUV422:
- return width << 1;
- default:
- return -EINVAL;
+ case I915_OVERLAY_YUV422:
+ return width << 1;
+ default:
+ return -EINVAL;
}
}
static int uv_hsubsampling(u32 format)
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
- case I915_OVERLAY_YUV422:
- case I915_OVERLAY_YUV420:
- return 2;
- case I915_OVERLAY_YUV411:
- case I915_OVERLAY_YUV410:
- return 4;
- default:
- return -EINVAL;
+ case I915_OVERLAY_YUV422:
+ case I915_OVERLAY_YUV420:
+ return 2;
+ case I915_OVERLAY_YUV411:
+ case I915_OVERLAY_YUV410:
+ return 4;
+ default:
+ return -EINVAL;
}
}
static int uv_vsubsampling(u32 format)
{
switch (format & I915_OVERLAY_DEPTH_MASK) {
- case I915_OVERLAY_YUV420:
- case I915_OVERLAY_YUV410:
- return 2;
- case I915_OVERLAY_YUV422:
- case I915_OVERLAY_YUV411:
- return 1;
- default:
- return -EINVAL;
+ case I915_OVERLAY_YUV420:
+ case I915_OVERLAY_YUV410:
+ return 2;
+ case I915_OVERLAY_YUV422:
+ case I915_OVERLAY_YUV411:
+ return 1;
+ default:
+ return -EINVAL;
}
}
static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width)
{
u32 mask, shift, ret;
- if (IS_I9XX(dev)) {
- mask = 0x3f;
- shift = 6;
- } else {
+ if (IS_GEN2(dev)) {
mask = 0x1f;
shift = 5;
+ } else {
+ mask = 0x3f;
+ shift = 6;
}
ret = ((offset + width + mask) >> shift) - (offset >> shift);
- if (IS_I9XX(dev))
+ if (!IS_GEN2(dev))
ret <<= 1;
ret -=1;
return ret << 2;
@@ -587,7 +583,9 @@ static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = {
0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060,
0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040,
0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020,
- 0xb000, 0x3000, 0x0800, 0x3000, 0xb000};
+ 0xb000, 0x3000, 0x0800, 0x3000, 0xb000
+};
+
static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60,
0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40,
@@ -597,7 +595,8 @@ static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0,
0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240,
0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0,
- 0x3000, 0x0800, 0x3000};
+ 0x3000, 0x0800, 0x3000
+};
static void update_polyphase_filter(struct overlay_registers *regs)
{
@@ -630,29 +629,31 @@ static bool update_scaling_factors(struct intel_overlay *overlay,
yscale = 1 << FP_SHIFT;
/*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/
- xscale_UV = xscale/uv_hscale;
- yscale_UV = yscale/uv_vscale;
- /* make the Y scale to UV scale ratio an exact multiply */
- xscale = xscale_UV * uv_hscale;
- yscale = yscale_UV * uv_vscale;
+ xscale_UV = xscale/uv_hscale;
+ yscale_UV = yscale/uv_vscale;
+ /* make the Y scale to UV scale ratio an exact multiply */
+ xscale = xscale_UV * uv_hscale;
+ yscale = yscale_UV * uv_vscale;
/*} else {
- xscale_UV = 0;
- yscale_UV = 0;
- }*/
+ xscale_UV = 0;
+ yscale_UV = 0;
+ }*/
if (xscale != overlay->old_xscale || yscale != overlay->old_yscale)
scale_changed = true;
overlay->old_xscale = xscale;
overlay->old_yscale = yscale;
- regs->YRGBSCALE = ((yscale & FRACT_MASK) << 20)
- | ((xscale >> FP_SHIFT) << 16)
- | ((xscale & FRACT_MASK) << 3);
- regs->UVSCALE = ((yscale_UV & FRACT_MASK) << 20)
- | ((xscale_UV >> FP_SHIFT) << 16)
- | ((xscale_UV & FRACT_MASK) << 3);
- regs->UVSCALEV = ((yscale >> FP_SHIFT) << 16)
- | ((yscale_UV >> FP_SHIFT) << 0);
+ regs->YRGBSCALE = (((yscale & FRACT_MASK) << 20) |
+ ((xscale >> FP_SHIFT) << 16) |
+ ((xscale & FRACT_MASK) << 3));
+
+ regs->UVSCALE = (((yscale_UV & FRACT_MASK) << 20) |
+ ((xscale_UV >> FP_SHIFT) << 16) |
+ ((xscale_UV & FRACT_MASK) << 3));
+
+ regs->UVSCALEV = ((((yscale >> FP_SHIFT) << 16) |
+ ((yscale_UV >> FP_SHIFT) << 0)));
if (scale_changed)
update_polyphase_filter(regs);
@@ -664,22 +665,28 @@ static void update_colorkey(struct intel_overlay *overlay,
struct overlay_registers *regs)
{
u32 key = overlay->color_key;
+
switch (overlay->crtc->base.fb->bits_per_pixel) {
- case 8:
- regs->DCLRKV = 0;
- regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE;
- case 16:
- if (overlay->crtc->base.fb->depth == 15) {
- regs->DCLRKV = RGB15_TO_COLORKEY(key);
- regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE;
- } else {
- regs->DCLRKV = RGB16_TO_COLORKEY(key);
- regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE;
- }
- case 24:
- case 32:
- regs->DCLRKV = key;
- regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE;
+ case 8:
+ regs->DCLRKV = 0;
+ regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE;
+ break;
+
+ case 16:
+ if (overlay->crtc->base.fb->depth == 15) {
+ regs->DCLRKV = RGB15_TO_COLORKEY(key);
+ regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE;
+ } else {
+ regs->DCLRKV = RGB16_TO_COLORKEY(key);
+ regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE;
+ }
+ break;
+
+ case 24:
+ case 32:
+ regs->DCLRKV = key;
+ regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE;
+ break;
}
}
@@ -689,48 +696,48 @@ static u32 overlay_cmd_reg(struct put_image_params *params)
if (params->format & I915_OVERLAY_YUV_PLANAR) {
switch (params->format & I915_OVERLAY_DEPTH_MASK) {
- case I915_OVERLAY_YUV422:
- cmd |= OCMD_YUV_422_PLANAR;
- break;
- case I915_OVERLAY_YUV420:
- cmd |= OCMD_YUV_420_PLANAR;
- break;
- case I915_OVERLAY_YUV411:
- case I915_OVERLAY_YUV410:
- cmd |= OCMD_YUV_410_PLANAR;
- break;
+ case I915_OVERLAY_YUV422:
+ cmd |= OCMD_YUV_422_PLANAR;
+ break;
+ case I915_OVERLAY_YUV420:
+ cmd |= OCMD_YUV_420_PLANAR;
+ break;
+ case I915_OVERLAY_YUV411:
+ case I915_OVERLAY_YUV410:
+ cmd |= OCMD_YUV_410_PLANAR;
+ break;
}
} else { /* YUV packed */
switch (params->format & I915_OVERLAY_DEPTH_MASK) {
- case I915_OVERLAY_YUV422:
- cmd |= OCMD_YUV_422_PACKED;
- break;
- case I915_OVERLAY_YUV411:
- cmd |= OCMD_YUV_411_PACKED;
- break;
+ case I915_OVERLAY_YUV422:
+ cmd |= OCMD_YUV_422_PACKED;
+ break;
+ case I915_OVERLAY_YUV411:
+ cmd |= OCMD_YUV_411_PACKED;
+ break;
}
switch (params->format & I915_OVERLAY_SWAP_MASK) {
- case I915_OVERLAY_NO_SWAP:
- break;
- case I915_OVERLAY_UV_SWAP:
- cmd |= OCMD_UV_SWAP;
- break;
- case I915_OVERLAY_Y_SWAP:
- cmd |= OCMD_Y_SWAP;
- break;
- case I915_OVERLAY_Y_AND_UV_SWAP:
- cmd |= OCMD_Y_AND_UV_SWAP;
- break;
+ case I915_OVERLAY_NO_SWAP:
+ break;
+ case I915_OVERLAY_UV_SWAP:
+ cmd |= OCMD_UV_SWAP;
+ break;
+ case I915_OVERLAY_Y_SWAP:
+ cmd |= OCMD_Y_SWAP;
+ break;
+ case I915_OVERLAY_Y_AND_UV_SWAP:
+ cmd |= OCMD_Y_AND_UV_SWAP;
+ break;
}
}
return cmd;
}
-int intel_overlay_do_put_image(struct intel_overlay *overlay,
- struct drm_gem_object *new_bo,
- struct put_image_params *params)
+static int intel_overlay_do_put_image(struct intel_overlay *overlay,
+ struct drm_gem_object *new_bo,
+ struct put_image_params *params)
{
int ret, tmp_width;
struct overlay_registers *regs;
@@ -755,24 +762,24 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay,
goto out_unpin;
if (!overlay->active) {
- regs = intel_overlay_map_regs_atomic(overlay);
+ regs = intel_overlay_map_regs(overlay);
if (!regs) {
ret = -ENOMEM;
goto out_unpin;
}
regs->OCONFIG = OCONF_CC_OUT_8BIT;
- if (IS_I965GM(overlay->dev))
+ if (IS_GEN4(overlay->dev))
regs->OCONFIG |= OCONF_CSC_MODE_BT709;
regs->OCONFIG |= overlay->crtc->pipe == 0 ?
OCONF_PIPE_A : OCONF_PIPE_B;
- intel_overlay_unmap_regs_atomic(overlay);
+ intel_overlay_unmap_regs(overlay, regs);
ret = intel_overlay_on(overlay);
if (ret != 0)
goto out_unpin;
}
- regs = intel_overlay_map_regs_atomic(overlay);
+ regs = intel_overlay_map_regs(overlay);
if (!regs) {
ret = -ENOMEM;
goto out_unpin;
@@ -788,7 +795,7 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay,
regs->SWIDTH = params->src_w;
regs->SWIDTHSW = calc_swidthsw(overlay->dev,
- params->offset_Y, tmp_width);
+ params->offset_Y, tmp_width);
regs->SHEIGHT = params->src_h;
regs->OBUF_0Y = bo_priv->gtt_offset + params-> offset_Y;
regs->OSTRIDE = params->stride_Y;
@@ -799,9 +806,9 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay,
u32 tmp_U, tmp_V;
regs->SWIDTH |= (params->src_w/uv_hscale) << 16;
tmp_U = calc_swidthsw(overlay->dev, params->offset_U,
- params->src_w/uv_hscale);
+ params->src_w/uv_hscale);
tmp_V = calc_swidthsw(overlay->dev, params->offset_V,
- params->src_w/uv_hscale);
+ params->src_w/uv_hscale);
regs->SWIDTHSW |= max_t(u32, tmp_U, tmp_V) << 16;
regs->SHEIGHT |= (params->src_h/uv_vscale) << 16;
regs->OBUF_0U = bo_priv->gtt_offset + params->offset_U;
@@ -815,9 +822,11 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay,
regs->OCMD = overlay_cmd_reg(params);
- intel_overlay_unmap_regs_atomic(overlay);
+ intel_overlay_unmap_regs(overlay, regs);
- intel_overlay_continue(overlay, scale_changed);
+ ret = intel_overlay_continue(overlay, scale_changed);
+ if (ret)
+ goto out_unpin;
overlay->old_vid_bo = overlay->vid_bo;
overlay->vid_bo = to_intel_bo(new_bo);
@@ -829,20 +838,19 @@ out_unpin:
return ret;
}
-int intel_overlay_switch_off(struct intel_overlay *overlay)
+int intel_overlay_switch_off(struct intel_overlay *overlay,
+ bool interruptible)
{
- int ret;
struct overlay_registers *regs;
struct drm_device *dev = overlay->dev;
+ int ret;
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
- if (overlay->hw_wedged) {
- ret = intel_overlay_recover_from_interrupt(overlay, 1);
- if (ret != 0)
- return ret;
- }
+ ret = intel_overlay_recover_from_interrupt(overlay, interruptible);
+ if (ret != 0)
+ return ret;
if (!overlay->active)
return 0;
@@ -851,33 +859,29 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
if (ret != 0)
return ret;
- regs = intel_overlay_map_regs_atomic(overlay);
+ regs = intel_overlay_map_regs(overlay);
regs->OCMD = 0;
- intel_overlay_unmap_regs_atomic(overlay);
+ intel_overlay_unmap_regs(overlay, regs);
- ret = intel_overlay_off(overlay);
+ ret = intel_overlay_off(overlay, interruptible);
if (ret != 0)
return ret;
intel_overlay_off_tail(overlay);
-
return 0;
}
static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
struct intel_crtc *crtc)
{
- drm_i915_private_t *dev_priv = overlay->dev->dev_private;
- u32 pipeconf;
- int pipeconf_reg = (crtc->pipe == 0) ? PIPEACONF : PIPEBCONF;
+ drm_i915_private_t *dev_priv = overlay->dev->dev_private;
- if (!crtc->base.enabled || crtc->dpms_mode != DRM_MODE_DPMS_ON)
+ if (!crtc->active)
return -EINVAL;
- pipeconf = I915_READ(pipeconf_reg);
-
/* can't use the overlay with double wide pipe */
- if (!IS_I965G(overlay->dev) && pipeconf & PIPEACONF_DOUBLE_WIDE)
+ if (INTEL_INFO(overlay->dev)->gen < 4 &&
+ (I915_READ(PIPECONF(crtc->pipe)) & (PIPECONF_DOUBLE_WIDE | PIPECONF_ENABLE)) != PIPECONF_ENABLE)
return -EINVAL;
return 0;
@@ -886,20 +890,22 @@ static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- u32 ratio;
+ drm_i915_private_t *dev_priv = dev->dev_private;
u32 pfit_control = I915_READ(PFIT_CONTROL);
+ u32 ratio;
/* XXX: This is not the same logic as in the xorg driver, but more in
- * line with the intel documentation for the i965 */
- if (!IS_I965G(dev) && (pfit_control & VERT_AUTO_SCALE)) {
- ratio = I915_READ(PFIT_AUTO_RATIOS) >> PFIT_VERT_SCALE_SHIFT;
- } else { /* on i965 use the PGM reg to read out the autoscaler values */
- ratio = I915_READ(PFIT_PGM_RATIOS);
- if (IS_I965G(dev))
- ratio >>= PFIT_VERT_SCALE_SHIFT_965;
+ * line with the intel documentation for the i965
+ */
+ if (INTEL_INFO(dev)->gen >= 4) {
+ /* on i965 use the PGM reg to read out the autoscaler values */
+ ratio = I915_READ(PFIT_PGM_RATIOS) >> PFIT_VERT_SCALE_SHIFT_965;
+ } else {
+ if (pfit_control & VERT_AUTO_SCALE)
+ ratio = I915_READ(PFIT_AUTO_RATIOS);
else
- ratio >>= PFIT_VERT_SCALE_SHIFT;
+ ratio = I915_READ(PFIT_PGM_RATIOS);
+ ratio >>= PFIT_VERT_SCALE_SHIFT;
}
overlay->pfit_vscale_ratio = ratio;
@@ -910,12 +916,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->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)
return 0;
else
return -EINVAL;
@@ -940,53 +944,57 @@ static int check_overlay_src(struct drm_device *dev,
struct drm_intel_overlay_put_image *rec,
struct drm_gem_object *new_bo)
{
- u32 stride_mask;
- int depth;
int uv_hscale = uv_hsubsampling(rec->flags);
int uv_vscale = uv_vsubsampling(rec->flags);
- size_t tmp;
+ u32 stride_mask, depth, tmp;
/* check src dimensions */
if (IS_845G(dev) || IS_I830(dev)) {
- if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY
- || rec->src_width > IMAGE_MAX_WIDTH_LEGACY)
+ if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY ||
+ rec->src_width > IMAGE_MAX_WIDTH_LEGACY)
return -EINVAL;
} else {
- if (rec->src_height > IMAGE_MAX_HEIGHT
- || rec->src_width > IMAGE_MAX_WIDTH)
+ if (rec->src_height > IMAGE_MAX_HEIGHT ||
+ rec->src_width > IMAGE_MAX_WIDTH)
return -EINVAL;
}
+
/* better safe than sorry, use 4 as the maximal subsampling ratio */
- if (rec->src_height < N_VERT_Y_TAPS*4
- || rec->src_width < N_HORIZ_Y_TAPS*4)
+ if (rec->src_height < N_VERT_Y_TAPS*4 ||
+ rec->src_width < N_HORIZ_Y_TAPS*4)
return -EINVAL;
/* check alignment constraints */
switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
- case I915_OVERLAY_RGB:
- /* not implemented */
+ case I915_OVERLAY_RGB:
+ /* not implemented */
+ return -EINVAL;
+
+ case I915_OVERLAY_YUV_PACKED:
+ if (uv_vscale != 1)
return -EINVAL;
- case I915_OVERLAY_YUV_PACKED:
- depth = packed_depth_bytes(rec->flags);
- if (uv_vscale != 1)
- return -EINVAL;
- if (depth < 0)
- return depth;
- /* ignore UV planes */
- rec->stride_UV = 0;
- rec->offset_U = 0;
- rec->offset_V = 0;
- /* check pixel alignment */
- if (rec->offset_Y % depth)
- return -EINVAL;
- break;
- case I915_OVERLAY_YUV_PLANAR:
- if (uv_vscale < 0 || uv_hscale < 0)
- return -EINVAL;
- /* no offset restrictions for planar formats */
- break;
- default:
+
+ depth = packed_depth_bytes(rec->flags);
+ if (depth < 0)
+ return depth;
+
+ /* ignore UV planes */
+ rec->stride_UV = 0;
+ rec->offset_U = 0;
+ rec->offset_V = 0;
+ /* check pixel alignment */
+ if (rec->offset_Y % depth)
return -EINVAL;
+ break;
+
+ case I915_OVERLAY_YUV_PLANAR:
+ if (uv_vscale < 0 || uv_hscale < 0)
+ return -EINVAL;
+ /* no offset restrictions for planar formats */
+ break;
+
+ default:
+ return -EINVAL;
}
if (rec->src_width % uv_hscale)
@@ -1000,47 +1008,74 @@ static int check_overlay_src(struct drm_device *dev,
if (rec->stride_Y & stride_mask || rec->stride_UV & stride_mask)
return -EINVAL;
- if (IS_I965G(dev) && rec->stride_Y < 512)
+ if (IS_GEN4(dev) && rec->stride_Y < 512)
return -EINVAL;
tmp = (rec->flags & I915_OVERLAY_TYPE_MASK) == I915_OVERLAY_YUV_PLANAR ?
- 4 : 8;
- if (rec->stride_Y > tmp*1024 || rec->stride_UV > 2*1024)
+ 4096 : 8192;
+ if (rec->stride_Y > tmp || rec->stride_UV > 2*1024)
return -EINVAL;
/* check buffer dimensions */
switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
- case I915_OVERLAY_RGB:
- case I915_OVERLAY_YUV_PACKED:
- /* always 4 Y values per depth pixels */
- if (packed_width_bytes(rec->flags, rec->src_width)
- > rec->stride_Y)
- return -EINVAL;
-
- tmp = rec->stride_Y*rec->src_height;
- if (rec->offset_Y + tmp > new_bo->size)
- return -EINVAL;
- break;
- case I915_OVERLAY_YUV_PLANAR:
- if (rec->src_width > rec->stride_Y)
- return -EINVAL;
- if (rec->src_width/uv_hscale > rec->stride_UV)
- return -EINVAL;
-
- tmp = rec->stride_Y*rec->src_height;
- if (rec->offset_Y + tmp > new_bo->size)
- return -EINVAL;
- tmp = rec->stride_UV*rec->src_height;
- tmp /= uv_vscale;
- if (rec->offset_U + tmp > new_bo->size
- || rec->offset_V + tmp > new_bo->size)
- return -EINVAL;
- break;
+ case I915_OVERLAY_RGB:
+ case I915_OVERLAY_YUV_PACKED:
+ /* always 4 Y values per depth pixels */
+ if (packed_width_bytes(rec->flags, rec->src_width) > rec->stride_Y)
+ return -EINVAL;
+
+ tmp = rec->stride_Y*rec->src_height;
+ if (rec->offset_Y + tmp > new_bo->size)
+ return -EINVAL;
+ break;
+
+ case I915_OVERLAY_YUV_PLANAR:
+ if (rec->src_width > rec->stride_Y)
+ return -EINVAL;
+ if (rec->src_width/uv_hscale > rec->stride_UV)
+ return -EINVAL;
+
+ tmp = rec->stride_Y * rec->src_height;
+ if (rec->offset_Y + tmp > new_bo->size)
+ return -EINVAL;
+
+ tmp = rec->stride_UV * (rec->src_height / uv_vscale);
+ if (rec->offset_U + tmp > new_bo->size ||
+ rec->offset_V + tmp > new_bo->size)
+ return -EINVAL;
+ break;
}
return 0;
}
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int intel_panel_fitter_pipe(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pfit_control;
+
+ /* i830 doesn't have a panel fitter */
+ if (IS_I830(dev))
+ return -1;
+
+ pfit_control = I915_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 */
+ if (IS_GEN4(dev))
+ return (pfit_control >> 29) & 0x3;
+
+ /* older chips can only use pipe 1 */
+ return 1;
+}
+
int intel_overlay_put_image(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -1068,7 +1103,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
mutex_lock(&dev->mode_config.mutex);
mutex_lock(&dev->struct_mutex);
- ret = intel_overlay_switch_off(overlay);
+ ret = intel_overlay_switch_off(overlay, true);
mutex_unlock(&dev->struct_mutex);
mutex_unlock(&dev->mode_config.mutex);
@@ -1081,7 +1116,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
return -ENOMEM;
drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
- DRM_MODE_OBJECT_CRTC);
+ DRM_MODE_OBJECT_CRTC);
if (!drmmode_obj) {
ret = -ENOENT;
goto out_free;
@@ -1089,7 +1124,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
new_bo = drm_gem_object_lookup(dev, file_priv,
- put_image_rec->bo_handle);
+ put_image_rec->bo_handle);
if (!new_bo) {
ret = -ENOENT;
goto out_free;
@@ -1098,15 +1133,13 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
mutex_lock(&dev->mode_config.mutex);
mutex_lock(&dev->struct_mutex);
- if (overlay->hw_wedged) {
- ret = intel_overlay_recover_from_interrupt(overlay, 1);
- if (ret != 0)
- goto out_unlock;
- }
+ ret = intel_overlay_recover_from_interrupt(overlay, true);
+ if (ret != 0)
+ goto out_unlock;
if (overlay->crtc != crtc) {
struct drm_display_mode *mode = &crtc->base.mode;
- ret = intel_overlay_switch_off(overlay);
+ ret = intel_overlay_switch_off(overlay, true);
if (ret != 0)
goto out_unlock;
@@ -1117,9 +1150,9 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
overlay->crtc = crtc;
crtc->overlay = overlay;
- if (intel_panel_fitter_pipe(dev) == crtc->pipe
- /* and line to wide, i.e. one-line-mode */
- && mode->hdisplay > 1024) {
+ /* line too wide, i.e. one-line-mode */
+ if (mode->hdisplay > 1024 &&
+ intel_panel_fitter_pipe(dev) == crtc->pipe) {
overlay->pfit_active = 1;
update_pfit_vscale_ratio(overlay);
} else
@@ -1132,10 +1165,10 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
if (overlay->pfit_active) {
params->dst_y = ((((u32)put_image_rec->dst_y) << 12) /
- overlay->pfit_vscale_ratio);
+ overlay->pfit_vscale_ratio);
/* shifting right rounds downwards, so add 1 */
params->dst_h = ((((u32)put_image_rec->dst_height) << 12) /
- overlay->pfit_vscale_ratio) + 1;
+ overlay->pfit_vscale_ratio) + 1;
} else {
params->dst_y = put_image_rec->dst_y;
params->dst_h = put_image_rec->dst_height;
@@ -1147,8 +1180,8 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
params->src_h = put_image_rec->src_height;
params->src_scan_w = put_image_rec->src_scan_width;
params->src_scan_h = put_image_rec->src_scan_height;
- if (params->src_scan_h > params->src_h
- || params->src_scan_w > params->src_w) {
+ if (params->src_scan_h > params->src_h ||
+ params->src_scan_w > params->src_w) {
ret = -EINVAL;
goto out_unlock;
}
@@ -1204,7 +1237,7 @@ static bool check_gamma_bounds(u32 gamma1, u32 gamma2)
return false;
for (i = 0; i < 3; i++) {
- if (((gamma1 >> i * 8) & 0xff) >= ((gamma2 >> i*8) & 0xff))
+ if (((gamma1 >> i*8) & 0xff) >= ((gamma2 >> i*8) & 0xff))
return false;
}
@@ -1225,16 +1258,18 @@ static bool check_gamma5_errata(u32 gamma5)
static int check_gamma(struct drm_intel_overlay_attrs *attrs)
{
- if (!check_gamma_bounds(0, attrs->gamma0)
- || !check_gamma_bounds(attrs->gamma0, attrs->gamma1)
- || !check_gamma_bounds(attrs->gamma1, attrs->gamma2)
- || !check_gamma_bounds(attrs->gamma2, attrs->gamma3)
- || !check_gamma_bounds(attrs->gamma3, attrs->gamma4)
- || !check_gamma_bounds(attrs->gamma4, attrs->gamma5)
- || !check_gamma_bounds(attrs->gamma5, 0x00ffffff))
+ if (!check_gamma_bounds(0, attrs->gamma0) ||
+ !check_gamma_bounds(attrs->gamma0, attrs->gamma1) ||
+ !check_gamma_bounds(attrs->gamma1, attrs->gamma2) ||
+ !check_gamma_bounds(attrs->gamma2, attrs->gamma3) ||
+ !check_gamma_bounds(attrs->gamma3, attrs->gamma4) ||
+ !check_gamma_bounds(attrs->gamma4, attrs->gamma5) ||
+ !check_gamma_bounds(attrs->gamma5, 0x00ffffff))
return -EINVAL;
+
if (!check_gamma5_errata(attrs->gamma5))
return -EINVAL;
+
return 0;
}
@@ -1261,13 +1296,14 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
mutex_lock(&dev->mode_config.mutex);
mutex_lock(&dev->struct_mutex);
+ ret = -EINVAL;
if (!(attrs->flags & I915_OVERLAY_UPDATE_ATTRS)) {
- attrs->color_key = overlay->color_key;
+ attrs->color_key = overlay->color_key;
attrs->brightness = overlay->brightness;
- attrs->contrast = overlay->contrast;
+ attrs->contrast = overlay->contrast;
attrs->saturation = overlay->saturation;
- if (IS_I9XX(dev)) {
+ if (!IS_GEN2(dev)) {
attrs->gamma0 = I915_READ(OGAMC0);
attrs->gamma1 = I915_READ(OGAMC1);
attrs->gamma2 = I915_READ(OGAMC2);
@@ -1275,29 +1311,20 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
attrs->gamma4 = I915_READ(OGAMC4);
attrs->gamma5 = I915_READ(OGAMC5);
}
- ret = 0;
} else {
- overlay->color_key = attrs->color_key;
- if (attrs->brightness >= -128 && attrs->brightness <= 127) {
- overlay->brightness = attrs->brightness;
- } else {
- ret = -EINVAL;
+ if (attrs->brightness < -128 || attrs->brightness > 127)
goto out_unlock;
- }
- if (attrs->contrast <= 255) {
- overlay->contrast = attrs->contrast;
- } else {
- ret = -EINVAL;
+ if (attrs->contrast > 255)
goto out_unlock;
- }
- if (attrs->saturation <= 1023) {
- overlay->saturation = attrs->saturation;
- } else {
- ret = -EINVAL;
+ if (attrs->saturation > 1023)
goto out_unlock;
- }
- regs = intel_overlay_map_regs_atomic(overlay);
+ overlay->color_key = attrs->color_key;
+ overlay->brightness = attrs->brightness;
+ overlay->contrast = attrs->contrast;
+ overlay->saturation = attrs->saturation;
+
+ regs = intel_overlay_map_regs(overlay);
if (!regs) {
ret = -ENOMEM;
goto out_unlock;
@@ -1305,13 +1332,11 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
update_reg_attrs(overlay, regs);
- intel_overlay_unmap_regs_atomic(overlay);
+ intel_overlay_unmap_regs(overlay, regs);
if (attrs->flags & I915_OVERLAY_UPDATE_GAMMA) {
- if (!IS_I9XX(dev)) {
- ret = -EINVAL;
+ if (IS_GEN2(dev))
goto out_unlock;
- }
if (overlay->active) {
ret = -EBUSY;
@@ -1319,7 +1344,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
}
ret = check_gamma(attrs);
- if (ret != 0)
+ if (ret)
goto out_unlock;
I915_WRITE(OGAMC0, attrs->gamma0);
@@ -1329,9 +1354,9 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
I915_WRITE(OGAMC4, attrs->gamma4);
I915_WRITE(OGAMC5, attrs->gamma5);
}
- ret = 0;
}
+ ret = 0;
out_unlock:
mutex_unlock(&dev->struct_mutex);
mutex_unlock(&dev->mode_config.mutex);
@@ -1347,7 +1372,7 @@ void intel_setup_overlay(struct drm_device *dev)
struct overlay_registers *regs;
int ret;
- if (!OVERLAY_EXISTS(dev))
+ if (!HAS_OVERLAY(dev))
return;
overlay = kzalloc(sizeof(struct intel_overlay), GFP_KERNEL);
@@ -1360,22 +1385,28 @@ void intel_setup_overlay(struct drm_device *dev)
goto out_free;
overlay->reg_bo = to_intel_bo(reg_bo);
- if (OVERLAY_NONPHYSICAL(dev)) {
- ret = i915_gem_object_pin(reg_bo, PAGE_SIZE);
- if (ret) {
- DRM_ERROR("failed to pin overlay register bo\n");
- goto out_free_bo;
- }
- overlay->flip_addr = overlay->reg_bo->gtt_offset;
- } else {
+ if (OVERLAY_NEEDS_PHYSICAL(dev)) {
ret = i915_gem_attach_phys_object(dev, reg_bo,
I915_GEM_PHYS_OVERLAY_REGS,
- 0);
+ PAGE_SIZE);
if (ret) {
DRM_ERROR("failed to attach phys overlay regs\n");
goto out_free_bo;
}
overlay->flip_addr = overlay->reg_bo->phys_obj->handle->busaddr;
+ } else {
+ ret = i915_gem_object_pin(reg_bo, PAGE_SIZE);
+ if (ret) {
+ DRM_ERROR("failed to pin overlay register bo\n");
+ goto out_free_bo;
+ }
+ overlay->flip_addr = overlay->reg_bo->gtt_offset;
+
+ ret = i915_gem_object_set_to_gtt_domain(reg_bo, true);
+ if (ret) {
+ DRM_ERROR("failed to move overlay register bo into the GTT\n");
+ goto out_unpin_bo;
+ }
}
/* init all values */
@@ -1384,21 +1415,22 @@ void intel_setup_overlay(struct drm_device *dev)
overlay->contrast = 75;
overlay->saturation = 146;
- regs = intel_overlay_map_regs_atomic(overlay);
+ regs = intel_overlay_map_regs(overlay);
if (!regs)
goto out_free_bo;
memset(regs, 0, sizeof(struct overlay_registers));
update_polyphase_filter(regs);
-
update_reg_attrs(overlay, regs);
- intel_overlay_unmap_regs_atomic(overlay);
+ intel_overlay_unmap_regs(overlay, regs);
dev_priv->overlay = overlay;
DRM_INFO("initialized overlay support\n");
return;
+out_unpin_bo:
+ i915_gem_object_unpin(reg_bo);
out_free_bo:
drm_gem_object_unreference(reg_bo);
out_free:
@@ -1408,18 +1440,23 @@ out_free:
void intel_cleanup_overlay(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_private_t *dev_priv = dev->dev_private;
- if (dev_priv->overlay) {
- /* The bo's should be free'd by the generic code already.
- * Furthermore modesetting teardown happens beforehand so the
- * hardware should be off already */
- BUG_ON(dev_priv->overlay->active);
+ if (!dev_priv->overlay)
+ return;
- kfree(dev_priv->overlay);
- }
+ /* The bo's should be free'd by the generic code already.
+ * Furthermore modesetting teardown happens beforehand so the
+ * hardware should be off already */
+ BUG_ON(dev_priv->overlay->active);
+
+ drm_gem_object_unreference_unlocked(&dev_priv->overlay->reg_bo->base);
+ kfree(dev_priv->overlay);
}
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
struct intel_overlay_error_state {
struct overlay_registers regs;
unsigned long base;
@@ -1427,6 +1464,29 @@ struct intel_overlay_error_state {
u32 isr;
};
+static struct overlay_registers *
+intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
+{
+ drm_i915_private_t *dev_priv = overlay->dev->dev_private;
+ struct overlay_registers *regs;
+
+ if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
+ regs = overlay->reg_bo->phys_obj->handle->vaddr;
+ else
+ regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+ overlay->reg_bo->gtt_offset);
+
+ return regs;
+}
+
+static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay,
+ struct overlay_registers *regs)
+{
+ if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev))
+ io_mapping_unmap_atomic(regs);
+}
+
+
struct intel_overlay_error_state *
intel_overlay_capture_error_state(struct drm_device *dev)
{
@@ -1444,17 +1504,17 @@ intel_overlay_capture_error_state(struct drm_device *dev)
error->dovsta = I915_READ(DOVSTA);
error->isr = I915_READ(ISR);
- if (OVERLAY_NONPHYSICAL(overlay->dev))
- error->base = (long) overlay->reg_bo->gtt_offset;
- else
+ if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
error->base = (long) overlay->reg_bo->phys_obj->handle->vaddr;
+ else
+ error->base = (long) overlay->reg_bo->gtt_offset;
regs = intel_overlay_map_regs_atomic(overlay);
if (!regs)
goto err;
memcpy_fromio(&error->regs, regs, sizeof(struct overlay_registers));
- intel_overlay_unmap_regs_atomic(overlay);
+ intel_overlay_unmap_regs_atomic(overlay, regs);
return error;
@@ -1515,3 +1575,4 @@ intel_overlay_print_error_state(struct seq_file *m, struct intel_overlay_error_s
P(UVSCALEV);
#undef P
}
+#endif
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index e7f5299d9d57..92ff8f385278 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -30,6 +30,8 @@
#include "intel_drv.h"
+#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
+
void
intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode)
@@ -109,3 +111,110 @@ done:
dev_priv->pch_pf_pos = (x << 16) | y;
dev_priv->pch_pf_size = (width << 16) | height;
}
+
+static int is_backlight_combination_mode(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (INTEL_INFO(dev)->gen >= 4)
+ return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
+
+ if (IS_GEN2(dev))
+ return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
+
+ return 0;
+}
+
+u32 intel_panel_get_max_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 max;
+
+ if (HAS_PCH_SPLIT(dev)) {
+ max = I915_READ(BLC_PWM_PCH_CTL2) >> 16;
+ } else {
+ max = I915_READ(BLC_PWM_CTL);
+ if (IS_PINEVIEW(dev)) {
+ max >>= 17;
+ } else {
+ max >>= 16;
+ if (INTEL_INFO(dev)->gen < 4)
+ max &= ~1;
+ }
+
+ if (is_backlight_combination_mode(dev))
+ max *= 0xff;
+ }
+
+ if (max == 0) {
+ /* XXX add code here to query mode clock or hardware clock
+ * and program max PWM appropriately.
+ */
+ DRM_ERROR("fixme: max PWM is zero.\n");
+ max = 1;
+ }
+
+ DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
+ return max;
+}
+
+u32 intel_panel_get_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val;
+
+ if (HAS_PCH_SPLIT(dev)) {
+ val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
+ } else {
+ val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
+ if (IS_PINEVIEW(dev))
+ val >>= 1;
+
+ if (is_backlight_combination_mode(dev)){
+ u8 lbpc;
+
+ val &= ~1;
+ pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);
+ val *= lbpc;
+ val >>= 1;
+ }
+ }
+
+ DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
+ return val;
+}
+
+static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+ I915_WRITE(BLC_PWM_CPU_CTL, val | level);
+}
+
+void intel_panel_set_backlight(struct drm_device *dev, u32 level)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 tmp;
+
+ DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
+
+ if (HAS_PCH_SPLIT(dev))
+ return intel_pch_panel_set_backlight(dev, level);
+
+ if (is_backlight_combination_mode(dev)){
+ u32 max = intel_panel_get_max_backlight(dev);
+ u8 lpbc;
+
+ lpbc = level * 0xfe / max + 1;
+ level /= lpbc;
+ pci_write_config_byte(dev->pdev, PCI_LBPC, lpbc);
+ }
+
+ tmp = I915_READ(BLC_PWM_CTL);
+ if (IS_PINEVIEW(dev)) {
+ tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
+ level <<= 1;
+ } else
+ tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
+ I915_WRITE(BLC_PWM_CTL, tmp | level);
+}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index cb3508f78bc3..09f2dc353ae2 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -32,6 +32,7 @@
#include "i915_drv.h"
#include "i915_drm.h"
#include "i915_trace.h"
+#include "intel_drv.h"
static u32 i915_gem_get_seqno(struct drm_device *dev)
{
@@ -49,9 +50,9 @@ static u32 i915_gem_get_seqno(struct drm_device *dev)
static void
render_ring_flush(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- u32 invalidate_domains,
- u32 flush_domains)
+ struct intel_ring_buffer *ring,
+ u32 invalidate_domains,
+ u32 flush_domains)
{
drm_i915_private_t *dev_priv = dev->dev_private;
u32 cmd;
@@ -97,7 +98,7 @@ render_ring_flush(struct drm_device *dev,
if ((invalidate_domains|flush_domains) &
I915_GEM_DOMAIN_RENDER)
cmd &= ~MI_NO_WRITE_FLUSH;
- if (!IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen < 4) {
/*
* On the 965, the sampler cache always gets flushed
* and this bit is reserved.
@@ -118,38 +119,26 @@ render_ring_flush(struct drm_device *dev,
}
}
-static unsigned int render_ring_get_head(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- return I915_READ(PRB0_HEAD) & HEAD_ADDR;
-}
-
-static unsigned int render_ring_get_tail(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+static void ring_write_tail(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ u32 value)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- return I915_READ(PRB0_TAIL) & TAIL_ADDR;
+ I915_WRITE_TAIL(ring, value);
}
-static unsigned int render_ring_get_active_head(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+u32 intel_ring_get_active_head(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- u32 acthd_reg = IS_I965G(dev) ? ACTHD_I965 : ACTHD;
+ u32 acthd_reg = INTEL_INFO(dev)->gen >= 4 ?
+ RING_ACTHD(ring->mmio_base) : ACTHD;
return I915_READ(acthd_reg);
}
-static void render_ring_advance_ring(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- I915_WRITE(PRB0_TAIL, ring->tail);
-}
-
static int init_ring_common(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
u32 head;
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -157,57 +146,57 @@ static int init_ring_common(struct drm_device *dev,
obj_priv = to_intel_bo(ring->gem_object);
/* Stop the ring if it's running. */
- I915_WRITE(ring->regs.ctl, 0);
- I915_WRITE(ring->regs.head, 0);
- I915_WRITE(ring->regs.tail, 0);
+ I915_WRITE_CTL(ring, 0);
+ I915_WRITE_HEAD(ring, 0);
+ ring->write_tail(dev, ring, 0);
/* Initialize the ring. */
- I915_WRITE(ring->regs.start, obj_priv->gtt_offset);
- head = ring->get_head(dev, ring);
+ I915_WRITE_START(ring, obj_priv->gtt_offset);
+ head = I915_READ_HEAD(ring) & HEAD_ADDR;
/* G45 ring initialization fails to reset head to zero */
if (head != 0) {
DRM_ERROR("%s head not reset to zero "
"ctl %08x head %08x tail %08x start %08x\n",
ring->name,
- I915_READ(ring->regs.ctl),
- I915_READ(ring->regs.head),
- I915_READ(ring->regs.tail),
- I915_READ(ring->regs.start));
+ I915_READ_CTL(ring),
+ I915_READ_HEAD(ring),
+ I915_READ_TAIL(ring),
+ I915_READ_START(ring));
- I915_WRITE(ring->regs.head, 0);
+ I915_WRITE_HEAD(ring, 0);
DRM_ERROR("%s head forced to zero "
"ctl %08x head %08x tail %08x start %08x\n",
ring->name,
- I915_READ(ring->regs.ctl),
- I915_READ(ring->regs.head),
- I915_READ(ring->regs.tail),
- I915_READ(ring->regs.start));
+ I915_READ_CTL(ring),
+ I915_READ_HEAD(ring),
+ I915_READ_TAIL(ring),
+ I915_READ_START(ring));
}
- I915_WRITE(ring->regs.ctl,
+ I915_WRITE_CTL(ring,
((ring->gem_object->size - PAGE_SIZE) & RING_NR_PAGES)
| RING_NO_REPORT | RING_VALID);
- head = I915_READ(ring->regs.head) & HEAD_ADDR;
+ head = I915_READ_HEAD(ring) & HEAD_ADDR;
/* If the head is still not zero, the ring is dead */
if (head != 0) {
DRM_ERROR("%s initialization failed "
"ctl %08x head %08x tail %08x start %08x\n",
ring->name,
- I915_READ(ring->regs.ctl),
- I915_READ(ring->regs.head),
- I915_READ(ring->regs.tail),
- I915_READ(ring->regs.start));
+ I915_READ_CTL(ring),
+ I915_READ_HEAD(ring),
+ I915_READ_TAIL(ring),
+ I915_READ_START(ring));
return -EIO;
}
if (!drm_core_check_feature(dev, DRIVER_MODESET))
i915_kernel_lost_context(dev);
else {
- ring->head = ring->get_head(dev, ring);
- ring->tail = ring->get_tail(dev, ring);
+ ring->head = I915_READ_HEAD(ring) & HEAD_ADDR;
+ ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
ring->space = ring->head - (ring->tail + 8);
if (ring->space < 0)
ring->space += ring->size;
@@ -216,13 +205,13 @@ static int init_ring_common(struct drm_device *dev,
}
static int init_render_ring(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int ret = init_ring_common(dev, ring);
int mode;
- if (IS_I9XX(dev) && !IS_GEN3(dev)) {
+ if (INTEL_INFO(dev)->gen > 3) {
mode = VS_TIMER_DISPATCH << 16 | VS_TIMER_DISPATCH;
if (IS_GEN6(dev))
mode |= MI_FLUSH_ENABLE << 16 | MI_FLUSH_ENABLE;
@@ -250,9 +239,8 @@ do { \
*/
static u32
render_ring_add_request(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- struct drm_file *file_priv,
- u32 flush_domains)
+ struct intel_ring_buffer *ring,
+ u32 flush_domains)
{
drm_i915_private_t *dev_priv = dev->dev_private;
u32 seqno;
@@ -315,8 +303,8 @@ render_ring_add_request(struct drm_device *dev,
}
static u32
-render_ring_get_gem_seqno(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+render_ring_get_seqno(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
if (HAS_PIPE_CONTROL(dev))
@@ -327,7 +315,7 @@ render_ring_get_gem_seqno(struct drm_device *dev,
static void
render_ring_get_user_irq(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags;
@@ -344,7 +332,7 @@ render_ring_get_user_irq(struct drm_device *dev,
static void
render_ring_put_user_irq(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags;
@@ -360,21 +348,23 @@ render_ring_put_user_irq(struct drm_device *dev,
spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
}
-static void render_setup_status_page(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+void intel_ring_setup_status_page(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
if (IS_GEN6(dev)) {
- I915_WRITE(HWS_PGA_GEN6, ring->status_page.gfx_addr);
- I915_READ(HWS_PGA_GEN6); /* posting read */
+ I915_WRITE(RING_HWS_PGA_GEN6(ring->mmio_base),
+ ring->status_page.gfx_addr);
+ I915_READ(RING_HWS_PGA_GEN6(ring->mmio_base)); /* posting read */
} else {
- I915_WRITE(HWS_PGA, ring->status_page.gfx_addr);
- I915_READ(HWS_PGA); /* posting read */
+ I915_WRITE(RING_HWS_PGA(ring->mmio_base),
+ ring->status_page.gfx_addr);
+ I915_READ(RING_HWS_PGA(ring->mmio_base)); /* posting read */
}
}
-void
+static void
bsd_ring_flush(struct drm_device *dev,
struct intel_ring_buffer *ring,
u32 invalidate_domains,
@@ -386,45 +376,16 @@ bsd_ring_flush(struct drm_device *dev,
intel_ring_advance(dev, ring);
}
-static inline unsigned int bsd_ring_get_head(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- return I915_READ(BSD_RING_HEAD) & HEAD_ADDR;
-}
-
-static inline unsigned int bsd_ring_get_tail(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- return I915_READ(BSD_RING_TAIL) & TAIL_ADDR;
-}
-
-static inline unsigned int bsd_ring_get_active_head(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- return I915_READ(BSD_RING_ACTHD);
-}
-
-static inline void bsd_ring_advance_ring(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- I915_WRITE(BSD_RING_TAIL, ring->tail);
-}
-
static int init_bsd_ring(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
return init_ring_common(dev, ring);
}
static u32
-bsd_ring_add_request(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- struct drm_file *file_priv,
- u32 flush_domains)
+ring_add_request(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ u32 flush_domains)
{
u32 seqno;
@@ -443,40 +404,32 @@ bsd_ring_add_request(struct drm_device *dev,
return seqno;
}
-static void bsd_setup_status_page(struct drm_device *dev,
- struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- I915_WRITE(BSD_HWS_PGA, ring->status_page.gfx_addr);
- I915_READ(BSD_HWS_PGA);
-}
-
static void
bsd_ring_get_user_irq(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
/* do nothing */
}
static void
bsd_ring_put_user_irq(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
/* do nothing */
}
static u32
-bsd_ring_get_gem_seqno(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ring_status_page_get_seqno(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
{
return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
}
static int
-bsd_ring_dispatch_gem_execbuffer(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- struct drm_i915_gem_execbuffer2 *exec,
- struct drm_clip_rect *cliprects,
- uint64_t exec_offset)
+ring_dispatch_gem_execbuffer(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ struct drm_i915_gem_execbuffer2 *exec,
+ struct drm_clip_rect *cliprects,
+ uint64_t exec_offset)
{
uint32_t exec_start;
exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
@@ -488,13 +441,12 @@ bsd_ring_dispatch_gem_execbuffer(struct drm_device *dev,
return 0;
}
-
static int
render_ring_dispatch_gem_execbuffer(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- struct drm_i915_gem_execbuffer2 *exec,
- struct drm_clip_rect *cliprects,
- uint64_t exec_offset)
+ struct intel_ring_buffer *ring,
+ struct drm_i915_gem_execbuffer2 *exec,
+ struct drm_clip_rect *cliprects,
+ uint64_t exec_offset)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int nbox = exec->num_cliprects;
@@ -523,8 +475,8 @@ render_ring_dispatch_gem_execbuffer(struct drm_device *dev,
intel_ring_emit(dev, ring, exec_start + exec_len - 4);
intel_ring_emit(dev, ring, 0);
} else {
- intel_ring_begin(dev, ring, 4);
- if (IS_I965G(dev)) {
+ intel_ring_begin(dev, ring, 2);
+ if (INTEL_INFO(dev)->gen >= 4) {
intel_ring_emit(dev, ring,
MI_BATCH_BUFFER_START | (2 << 6)
| MI_BATCH_NON_SECURE_I965);
@@ -539,7 +491,7 @@ render_ring_dispatch_gem_execbuffer(struct drm_device *dev,
intel_ring_advance(dev, ring);
}
- if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
+ if (IS_G4X(dev) || IS_GEN5(dev)) {
intel_ring_begin(dev, ring, 2);
intel_ring_emit(dev, ring, MI_FLUSH |
MI_NO_WRITE_FLUSH |
@@ -553,7 +505,7 @@ render_ring_dispatch_gem_execbuffer(struct drm_device *dev,
}
static void cleanup_status_page(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
@@ -573,7 +525,7 @@ static void cleanup_status_page(struct drm_device *dev,
}
static int init_status_page(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
@@ -603,7 +555,7 @@ static int init_status_page(struct drm_device *dev,
ring->status_page.obj = obj;
memset(ring->status_page.page_addr, 0, PAGE_SIZE);
- ring->setup_status_page(dev, ring);
+ intel_ring_setup_status_page(dev, ring);
DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n",
ring->name, ring->status_page.gfx_addr);
@@ -617,15 +569,18 @@ err:
return ret;
}
-
int intel_init_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv;
struct drm_gem_object *obj;
int ret;
ring->dev = dev;
+ INIT_LIST_HEAD(&ring->active_list);
+ INIT_LIST_HEAD(&ring->request_list);
+ INIT_LIST_HEAD(&ring->gpu_write_list);
if (I915_NEED_GFX_HWS(dev)) {
ret = init_status_page(dev, ring);
@@ -642,7 +597,7 @@ int intel_init_ring_buffer(struct drm_device *dev,
ring->gem_object = obj;
- ret = i915_gem_object_pin(obj, ring->alignment);
+ ret = i915_gem_object_pin(obj, PAGE_SIZE);
if (ret)
goto err_unref;
@@ -668,14 +623,12 @@ int intel_init_ring_buffer(struct drm_device *dev,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
i915_kernel_lost_context(dev);
else {
- ring->head = ring->get_head(dev, ring);
- ring->tail = ring->get_tail(dev, ring);
+ ring->head = I915_READ_HEAD(ring) & HEAD_ADDR;
+ ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
ring->space = ring->head - (ring->tail + 8);
if (ring->space < 0)
ring->space += ring->size;
}
- INIT_LIST_HEAD(&ring->active_list);
- INIT_LIST_HEAD(&ring->request_list);
return ret;
err_unmap:
@@ -691,7 +644,7 @@ err_hws:
}
void intel_cleanup_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
if (ring->gem_object == NULL)
return;
@@ -704,8 +657,8 @@ void intel_cleanup_ring_buffer(struct drm_device *dev,
cleanup_status_page(dev, ring);
}
-int intel_wrap_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+static int intel_wrap_ring_buffer(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
{
unsigned int *virt;
int rem;
@@ -731,14 +684,15 @@ int intel_wrap_ring_buffer(struct drm_device *dev,
}
int intel_wait_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring, int n)
+ struct intel_ring_buffer *ring, int n)
{
unsigned long end;
+ drm_i915_private_t *dev_priv = dev->dev_private;
trace_i915_ring_wait_begin (dev);
end = jiffies + 3 * HZ;
do {
- ring->head = ring->get_head(dev, ring);
+ ring->head = I915_READ_HEAD(ring) & HEAD_ADDR;
ring->space = ring->head - (ring->tail + 8);
if (ring->space < 0)
ring->space += ring->size;
@@ -753,14 +707,15 @@ int intel_wait_ring_buffer(struct drm_device *dev,
master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
}
- yield();
+ msleep(1);
} while (!time_after(jiffies, end));
trace_i915_ring_wait_end (dev);
return -EBUSY;
}
void intel_ring_begin(struct drm_device *dev,
- struct intel_ring_buffer *ring, int num_dwords)
+ struct intel_ring_buffer *ring,
+ int num_dwords)
{
int n = 4*num_dwords;
if (unlikely(ring->tail + n > ring->size))
@@ -772,97 +727,181 @@ void intel_ring_begin(struct drm_device *dev,
}
void intel_ring_advance(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring)
{
ring->tail &= ring->size - 1;
- ring->advance_ring(dev, ring);
-}
-
-void intel_fill_struct(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- void *data,
- unsigned int len)
-{
- unsigned int *virt = ring->virtual_start + ring->tail;
- BUG_ON((len&~(4-1)) != 0);
- intel_ring_begin(dev, ring, len/4);
- memcpy(virt, data, len);
- ring->tail += len;
- ring->tail &= ring->size - 1;
- ring->space -= len;
- intel_ring_advance(dev, ring);
+ ring->write_tail(dev, ring, ring->tail);
}
-struct intel_ring_buffer render_ring = {
+static const struct intel_ring_buffer render_ring = {
.name = "render ring",
- .regs = {
- .ctl = PRB0_CTL,
- .head = PRB0_HEAD,
- .tail = PRB0_TAIL,
- .start = PRB0_START
- },
- .ring_flag = I915_EXEC_RENDER,
+ .id = RING_RENDER,
+ .mmio_base = RENDER_RING_BASE,
.size = 32 * PAGE_SIZE,
- .alignment = PAGE_SIZE,
- .virtual_start = NULL,
- .dev = NULL,
- .gem_object = NULL,
- .head = 0,
- .tail = 0,
- .space = 0,
- .user_irq_refcount = 0,
- .irq_gem_seqno = 0,
- .waiting_gem_seqno = 0,
- .setup_status_page = render_setup_status_page,
.init = init_render_ring,
- .get_head = render_ring_get_head,
- .get_tail = render_ring_get_tail,
- .get_active_head = render_ring_get_active_head,
- .advance_ring = render_ring_advance_ring,
+ .write_tail = ring_write_tail,
.flush = render_ring_flush,
.add_request = render_ring_add_request,
- .get_gem_seqno = render_ring_get_gem_seqno,
+ .get_seqno = render_ring_get_seqno,
.user_irq_get = render_ring_get_user_irq,
.user_irq_put = render_ring_put_user_irq,
.dispatch_gem_execbuffer = render_ring_dispatch_gem_execbuffer,
- .status_page = {NULL, 0, NULL},
- .map = {0,}
};
/* ring buffer for bit-stream decoder */
-struct intel_ring_buffer bsd_ring = {
+static const struct intel_ring_buffer bsd_ring = {
.name = "bsd ring",
- .regs = {
- .ctl = BSD_RING_CTL,
- .head = BSD_RING_HEAD,
- .tail = BSD_RING_TAIL,
- .start = BSD_RING_START
- },
- .ring_flag = I915_EXEC_BSD,
+ .id = RING_BSD,
+ .mmio_base = BSD_RING_BASE,
.size = 32 * PAGE_SIZE,
- .alignment = PAGE_SIZE,
- .virtual_start = NULL,
- .dev = NULL,
- .gem_object = NULL,
- .head = 0,
- .tail = 0,
- .space = 0,
- .user_irq_refcount = 0,
- .irq_gem_seqno = 0,
- .waiting_gem_seqno = 0,
- .setup_status_page = bsd_setup_status_page,
.init = init_bsd_ring,
- .get_head = bsd_ring_get_head,
- .get_tail = bsd_ring_get_tail,
- .get_active_head = bsd_ring_get_active_head,
- .advance_ring = bsd_ring_advance_ring,
+ .write_tail = ring_write_tail,
.flush = bsd_ring_flush,
- .add_request = bsd_ring_add_request,
- .get_gem_seqno = bsd_ring_get_gem_seqno,
+ .add_request = ring_add_request,
+ .get_seqno = ring_status_page_get_seqno,
.user_irq_get = bsd_ring_get_user_irq,
.user_irq_put = bsd_ring_put_user_irq,
- .dispatch_gem_execbuffer = bsd_ring_dispatch_gem_execbuffer,
- .status_page = {NULL, 0, NULL},
- .map = {0,}
+ .dispatch_gem_execbuffer = ring_dispatch_gem_execbuffer,
};
+
+
+static void gen6_bsd_ring_write_tail(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ u32 value)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ /* Every tail move must follow the sequence below */
+ I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL,
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK |
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE);
+ I915_WRITE(GEN6_BSD_RNCID, 0x0);
+
+ if (wait_for((I915_READ(GEN6_BSD_SLEEP_PSMI_CONTROL) &
+ GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR) == 0,
+ 50))
+ DRM_ERROR("timed out waiting for IDLE Indicator\n");
+
+ I915_WRITE_TAIL(ring, value);
+ I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL,
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK |
+ GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE);
+}
+
+static void gen6_ring_flush(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ u32 invalidate_domains,
+ u32 flush_domains)
+{
+ intel_ring_begin(dev, ring, 4);
+ intel_ring_emit(dev, ring, MI_FLUSH_DW);
+ intel_ring_emit(dev, ring, 0);
+ intel_ring_emit(dev, ring, 0);
+ intel_ring_emit(dev, ring, 0);
+ intel_ring_advance(dev, ring);
+}
+
+static int
+gen6_ring_dispatch_gem_execbuffer(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ struct drm_i915_gem_execbuffer2 *exec,
+ struct drm_clip_rect *cliprects,
+ uint64_t exec_offset)
+{
+ uint32_t exec_start;
+
+ exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
+
+ intel_ring_begin(dev, ring, 2);
+ intel_ring_emit(dev, ring,
+ MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965);
+ /* bit0-7 is the length on GEN6+ */
+ intel_ring_emit(dev, ring, exec_start);
+ intel_ring_advance(dev, ring);
+
+ return 0;
+}
+
+/* ring buffer for Video Codec for Gen6+ */
+static const struct intel_ring_buffer gen6_bsd_ring = {
+ .name = "gen6 bsd ring",
+ .id = RING_BSD,
+ .mmio_base = GEN6_BSD_RING_BASE,
+ .size = 32 * PAGE_SIZE,
+ .init = init_bsd_ring,
+ .write_tail = gen6_bsd_ring_write_tail,
+ .flush = gen6_ring_flush,
+ .add_request = ring_add_request,
+ .get_seqno = ring_status_page_get_seqno,
+ .user_irq_get = bsd_ring_get_user_irq,
+ .user_irq_put = bsd_ring_put_user_irq,
+ .dispatch_gem_execbuffer = gen6_ring_dispatch_gem_execbuffer,
+};
+
+/* Blitter support (SandyBridge+) */
+
+static void
+blt_ring_get_user_irq(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
+{
+ /* do nothing */
+}
+static void
+blt_ring_put_user_irq(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
+{
+ /* do nothing */
+}
+
+static const struct intel_ring_buffer gen6_blt_ring = {
+ .name = "blt ring",
+ .id = RING_BLT,
+ .mmio_base = BLT_RING_BASE,
+ .size = 32 * PAGE_SIZE,
+ .init = init_ring_common,
+ .write_tail = ring_write_tail,
+ .flush = gen6_ring_flush,
+ .add_request = ring_add_request,
+ .get_seqno = ring_status_page_get_seqno,
+ .user_irq_get = blt_ring_get_user_irq,
+ .user_irq_put = blt_ring_put_user_irq,
+ .dispatch_gem_execbuffer = gen6_ring_dispatch_gem_execbuffer,
+};
+
+int intel_init_render_ring_buffer(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ dev_priv->render_ring = render_ring;
+
+ if (!I915_NEED_GFX_HWS(dev)) {
+ dev_priv->render_ring.status_page.page_addr
+ = dev_priv->status_page_dmah->vaddr;
+ memset(dev_priv->render_ring.status_page.page_addr,
+ 0, PAGE_SIZE);
+ }
+
+ return intel_init_ring_buffer(dev, &dev_priv->render_ring);
+}
+
+int intel_init_bsd_ring_buffer(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ if (IS_GEN6(dev))
+ dev_priv->bsd_ring = gen6_bsd_ring;
+ else
+ dev_priv->bsd_ring = bsd_ring;
+
+ return intel_init_ring_buffer(dev, &dev_priv->bsd_ring);
+}
+
+int intel_init_blt_ring_buffer(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ dev_priv->blt_ring = gen6_blt_ring;
+
+ return intel_init_ring_buffer(dev, &dev_priv->blt_ring);
+}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 525e7d3edda8..a05aff0e5764 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -7,25 +7,32 @@ struct intel_hw_status_page {
struct drm_gem_object *obj;
};
+#define I915_READ_TAIL(ring) I915_READ(RING_TAIL(ring->mmio_base))
+#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL(ring->mmio_base), val)
+#define I915_READ_START(ring) I915_READ(RING_START(ring->mmio_base))
+#define I915_WRITE_START(ring, val) I915_WRITE(RING_START(ring->mmio_base), val)
+#define I915_READ_HEAD(ring) I915_READ(RING_HEAD(ring->mmio_base))
+#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD(ring->mmio_base), val)
+#define I915_READ_CTL(ring) I915_READ(RING_CTL(ring->mmio_base))
+#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL(ring->mmio_base), val)
+
struct drm_i915_gem_execbuffer2;
struct intel_ring_buffer {
const char *name;
- struct ring_regs {
- u32 ctl;
- u32 head;
- u32 tail;
- u32 start;
- } regs;
- unsigned int ring_flag;
+ enum intel_ring_id {
+ RING_RENDER = 0x1,
+ RING_BSD = 0x2,
+ RING_BLT = 0x4,
+ } id;
+ u32 mmio_base;
unsigned long size;
- unsigned int alignment;
void *virtual_start;
struct drm_device *dev;
struct drm_gem_object *gem_object;
unsigned int head;
unsigned int tail;
- unsigned int space;
+ int space;
struct intel_hw_status_page status_page;
u32 irq_gem_seqno; /* last seq seem at irq time */
@@ -35,30 +42,22 @@ struct intel_ring_buffer {
struct intel_ring_buffer *ring);
void (*user_irq_put)(struct drm_device *dev,
struct intel_ring_buffer *ring);
- void (*setup_status_page)(struct drm_device *dev,
- struct intel_ring_buffer *ring);
int (*init)(struct drm_device *dev,
struct intel_ring_buffer *ring);
- unsigned int (*get_head)(struct drm_device *dev,
- struct intel_ring_buffer *ring);
- unsigned int (*get_tail)(struct drm_device *dev,
- struct intel_ring_buffer *ring);
- unsigned int (*get_active_head)(struct drm_device *dev,
- struct intel_ring_buffer *ring);
- void (*advance_ring)(struct drm_device *dev,
- struct intel_ring_buffer *ring);
+ void (*write_tail)(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ u32 value);
void (*flush)(struct drm_device *dev,
struct intel_ring_buffer *ring,
u32 invalidate_domains,
u32 flush_domains);
u32 (*add_request)(struct drm_device *dev,
struct intel_ring_buffer *ring,
- struct drm_file *file_priv,
u32 flush_domains);
- u32 (*get_gem_seqno)(struct drm_device *dev,
- struct intel_ring_buffer *ring);
+ u32 (*get_seqno)(struct drm_device *dev,
+ struct intel_ring_buffer *ring);
int (*dispatch_gem_execbuffer)(struct drm_device *dev,
struct intel_ring_buffer *ring,
struct drm_i915_gem_execbuffer2 *exec,
@@ -83,6 +82,20 @@ struct intel_ring_buffer {
*/
struct list_head request_list;
+ /**
+ * List of objects currently pending a GPU write flush.
+ *
+ * All elements on this list will belong to either the
+ * active_list or flushing_list, last_rendering_seqno can
+ * be used to differentiate between the two elements.
+ */
+ struct list_head gpu_write_list;
+
+ /**
+ * Do we have some not yet emitted requests outstanding?
+ */
+ bool outstanding_lazy_request;
+
wait_queue_head_t irq_queue;
drm_local_map_t map;
};
@@ -96,15 +109,13 @@ intel_read_status_page(struct intel_ring_buffer *ring,
}
int intel_init_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring);
+ struct intel_ring_buffer *ring);
void intel_cleanup_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring);
+ struct intel_ring_buffer *ring);
int intel_wait_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring, int n);
-int intel_wrap_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring);
+ struct intel_ring_buffer *ring, int n);
void intel_ring_begin(struct drm_device *dev,
- struct intel_ring_buffer *ring, int n);
+ struct intel_ring_buffer *ring, int n);
static inline void intel_ring_emit(struct drm_device *dev,
struct intel_ring_buffer *ring,
@@ -115,17 +126,19 @@ static inline void intel_ring_emit(struct drm_device *dev,
ring->tail += 4;
}
-void intel_fill_struct(struct drm_device *dev,
- struct intel_ring_buffer *ring,
- void *data,
- unsigned int len);
void intel_ring_advance(struct drm_device *dev,
struct intel_ring_buffer *ring);
u32 intel_ring_get_seqno(struct drm_device *dev,
struct intel_ring_buffer *ring);
-extern struct intel_ring_buffer render_ring;
-extern struct intel_ring_buffer bsd_ring;
+int intel_init_render_ring_buffer(struct drm_device *dev);
+int intel_init_bsd_ring_buffer(struct drm_device *dev);
+int intel_init_blt_ring_buffer(struct drm_device *dev);
+
+u32 intel_ring_get_active_head(struct drm_device *dev,
+ struct intel_ring_buffer *ring);
+void intel_ring_setup_status_page(struct drm_device *dev,
+ struct intel_ring_buffer *ring);
#endif /* _INTEL_RINGBUFFER_H_ */
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index ee73e428a84a..de158b76bcd5 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -65,8 +65,11 @@ static const char *tv_format_names[] = {
struct intel_sdvo {
struct intel_encoder base;
+ struct i2c_adapter *i2c;
u8 slave_addr;
+ struct i2c_adapter ddc;
+
/* Register for the SDVO device: SDVOB or SDVOC */
int sdvo_reg;
@@ -104,34 +107,24 @@ struct intel_sdvo {
* This is set if we treat the device as HDMI, instead of DVI.
*/
bool is_hdmi;
+ bool has_audio;
/**
- * This is set if we detect output of sdvo device as LVDS.
+ * This is set if we detect output of sdvo device as LVDS and
+ * have a valid fixed mode to use with the panel.
*/
bool is_lvds;
/**
- * This is sdvo flags for input timing.
- */
- uint8_t sdvo_flags;
-
- /**
* This is sdvo fixed pannel mode pointer
*/
struct drm_display_mode *sdvo_lvds_fixed_mode;
- /*
- * supported encoding mode, used to determine whether HDMI is
- * supported
- */
- struct intel_sdvo_encode encode;
-
/* DDC bus used by this SDVO encoder */
uint8_t ddc_bus;
- /* Mac mini hack -- use the same DDC as the analog connector */
- struct i2c_adapter *analog_ddc_bus;
-
+ /* Input timings for adjusted_mode */
+ struct intel_sdvo_dtd input_dtd;
};
struct intel_sdvo_connector {
@@ -140,11 +133,15 @@ struct intel_sdvo_connector {
/* Mark the type of connector */
uint16_t output_flag;
+ int force_audio;
+
/* This contains all current supported TV format */
u8 tv_format_supported[TV_FORMAT_NUM];
int format_supported_num;
struct drm_property *tv_format;
+ struct drm_property *force_audio_property;
+
/* add the property for the SDVO-TV */
struct drm_property *left;
struct drm_property *right;
@@ -186,9 +183,15 @@ struct intel_sdvo_connector {
u32 cur_dot_crawl, max_dot_crawl;
};
-static struct intel_sdvo *enc_to_intel_sdvo(struct drm_encoder *encoder)
+static struct intel_sdvo *to_intel_sdvo(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct intel_sdvo, base.base);
+}
+
+static struct intel_sdvo *intel_attached_sdvo(struct drm_connector *connector)
{
- return container_of(enc_to_intel_encoder(encoder), struct intel_sdvo, base);
+ return container_of(intel_attached_encoder(connector),
+ struct intel_sdvo, base);
}
static struct intel_sdvo_connector *to_intel_sdvo_connector(struct drm_connector *connector)
@@ -213,7 +216,7 @@ intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
*/
static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val)
{
- struct drm_device *dev = intel_sdvo->base.enc.dev;
+ struct drm_device *dev = intel_sdvo->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 bval = val, cval = val;
int i;
@@ -245,49 +248,29 @@ static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val)
static bool intel_sdvo_read_byte(struct intel_sdvo *intel_sdvo, u8 addr, u8 *ch)
{
- u8 out_buf[2] = { addr, 0 };
- u8 buf[2];
struct i2c_msg msgs[] = {
{
- .addr = intel_sdvo->slave_addr >> 1,
+ .addr = intel_sdvo->slave_addr,
.flags = 0,
.len = 1,
- .buf = out_buf,
+ .buf = &addr,
},
{
- .addr = intel_sdvo->slave_addr >> 1,
+ .addr = intel_sdvo->slave_addr,
.flags = I2C_M_RD,
.len = 1,
- .buf = buf,
+ .buf = ch,
}
};
int ret;
- if ((ret = i2c_transfer(intel_sdvo->base.i2c_bus, msgs, 2)) == 2)
- {
- *ch = buf[0];
+ if ((ret = i2c_transfer(intel_sdvo->i2c, msgs, 2)) == 2)
return true;
- }
DRM_DEBUG_KMS("i2c transfer returned %d\n", ret);
return false;
}
-static bool intel_sdvo_write_byte(struct intel_sdvo *intel_sdvo, int addr, u8 ch)
-{
- u8 out_buf[2] = { addr, ch };
- struct i2c_msg msgs[] = {
- {
- .addr = intel_sdvo->slave_addr >> 1,
- .flags = 0,
- .len = 2,
- .buf = out_buf,
- }
- };
-
- return i2c_transfer(intel_sdvo->base.i2c_bus, msgs, 1) == 1;
-}
-
#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
/** Mapping of command numbers to names, for debug output */
static const struct _sdvo_cmd_name {
@@ -432,22 +415,6 @@ static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd,
DRM_LOG_KMS("\n");
}
-static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
- const void *args, int args_len)
-{
- int i;
-
- intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
-
- for (i = 0; i < args_len; i++) {
- if (!intel_sdvo_write_byte(intel_sdvo, SDVO_I2C_ARG_0 - i,
- ((u8*)args)[i]))
- return false;
- }
-
- return intel_sdvo_write_byte(intel_sdvo, SDVO_I2C_OPCODE, cmd);
-}
-
static const char *cmd_status_names[] = {
"Power on",
"Success",
@@ -458,54 +425,115 @@ static const char *cmd_status_names[] = {
"Scaling not supported"
};
-static void intel_sdvo_debug_response(struct intel_sdvo *intel_sdvo,
- void *response, int response_len,
- u8 status)
+static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
+ const void *args, int args_len)
{
- int i;
+ u8 buf[args_len*2 + 2], status;
+ struct i2c_msg msgs[args_len + 3];
+ int i, ret;
- DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(intel_sdvo));
- for (i = 0; i < response_len; i++)
- DRM_LOG_KMS("%02X ", ((u8 *)response)[i]);
- for (; i < 8; i++)
- DRM_LOG_KMS(" ");
- if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
- DRM_LOG_KMS("(%s)", cmd_status_names[status]);
- else
- DRM_LOG_KMS("(??? %d)", status);
- DRM_LOG_KMS("\n");
+ intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
+
+ for (i = 0; i < args_len; i++) {
+ msgs[i].addr = intel_sdvo->slave_addr;
+ msgs[i].flags = 0;
+ msgs[i].len = 2;
+ msgs[i].buf = buf + 2 *i;
+ buf[2*i + 0] = SDVO_I2C_ARG_0 - i;
+ buf[2*i + 1] = ((u8*)args)[i];
+ }
+ msgs[i].addr = intel_sdvo->slave_addr;
+ msgs[i].flags = 0;
+ msgs[i].len = 2;
+ msgs[i].buf = buf + 2*i;
+ buf[2*i + 0] = SDVO_I2C_OPCODE;
+ buf[2*i + 1] = cmd;
+
+ /* the following two are to read the response */
+ status = SDVO_I2C_CMD_STATUS;
+ msgs[i+1].addr = intel_sdvo->slave_addr;
+ msgs[i+1].flags = 0;
+ msgs[i+1].len = 1;
+ msgs[i+1].buf = &status;
+
+ msgs[i+2].addr = intel_sdvo->slave_addr;
+ msgs[i+2].flags = I2C_M_RD;
+ msgs[i+2].len = 1;
+ msgs[i+2].buf = &status;
+
+ ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("I2c transfer returned %d\n", ret);
+ return false;
+ }
+ if (ret != i+3) {
+ /* failure in I2C transfer */
+ DRM_DEBUG_KMS("I2c transfer returned %d/%d\n", ret, i+3);
+ return false;
+ }
+
+ i = 3;
+ while (status == SDVO_CMD_STATUS_PENDING && i--) {
+ if (!intel_sdvo_read_byte(intel_sdvo,
+ SDVO_I2C_CMD_STATUS,
+ &status))
+ return false;
+ }
+ if (status != SDVO_CMD_STATUS_SUCCESS) {
+ DRM_DEBUG_KMS("command returns response %s [%d]\n",
+ status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP ? cmd_status_names[status] : "???",
+ status);
+ return false;
+ }
+
+ return true;
}
static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo,
void *response, int response_len)
{
- int i;
+ u8 retry = 5;
u8 status;
- u8 retry = 50;
-
- while (retry--) {
- /* Read the command response */
- for (i = 0; i < response_len; i++) {
- if (!intel_sdvo_read_byte(intel_sdvo,
- SDVO_I2C_RETURN_0 + i,
- &((u8 *)response)[i]))
- return false;
- }
+ int i;
- /* read the return status */
- if (!intel_sdvo_read_byte(intel_sdvo, SDVO_I2C_CMD_STATUS,
+ /*
+ * The documentation states that all commands will be
+ * processed within 15µs, and that we need only poll
+ * the status byte a maximum of 3 times in order for the
+ * command to be complete.
+ *
+ * Check 5 times in case the hardware failed to read the docs.
+ */
+ do {
+ if (!intel_sdvo_read_byte(intel_sdvo,
+ SDVO_I2C_CMD_STATUS,
&status))
return false;
+ } while (status == SDVO_CMD_STATUS_PENDING && --retry);
- intel_sdvo_debug_response(intel_sdvo, response, response_len,
- status);
- if (status != SDVO_CMD_STATUS_PENDING)
- break;
+ DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(intel_sdvo));
+ if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
+ DRM_LOG_KMS("(%s)", cmd_status_names[status]);
+ else
+ DRM_LOG_KMS("(??? %d)", status);
- mdelay(50);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ goto log_fail;
+
+ /* Read the command response */
+ for (i = 0; i < response_len; i++) {
+ if (!intel_sdvo_read_byte(intel_sdvo,
+ SDVO_I2C_RETURN_0 + i,
+ &((u8 *)response)[i]))
+ goto log_fail;
+ DRM_LOG_KMS(" %02X", ((u8 *)response)[i]);
}
+ DRM_LOG_KMS("\n");
+ return true;
- return status == SDVO_CMD_STATUS_SUCCESS;
+log_fail:
+ DRM_LOG_KMS("\n");
+ return false;
}
static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
@@ -518,71 +546,17 @@ static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
return 4;
}
-/**
- * Try to read the response after issuie the DDC switch command. But it
- * is noted that we must do the action of reading response and issuing DDC
- * switch command in one I2C transaction. Otherwise when we try to start
- * another I2C transaction after issuing the DDC bus switch, it will be
- * switched to the internal SDVO register.
- */
-static void intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
- u8 target)
+static bool intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
+ u8 ddc_bus)
{
- u8 out_buf[2], cmd_buf[2], ret_value[2], ret;
- struct i2c_msg msgs[] = {
- {
- .addr = intel_sdvo->slave_addr >> 1,
- .flags = 0,
- .len = 2,
- .buf = out_buf,
- },
- /* the following two are to read the response */
- {
- .addr = intel_sdvo->slave_addr >> 1,
- .flags = 0,
- .len = 1,
- .buf = cmd_buf,
- },
- {
- .addr = intel_sdvo->slave_addr >> 1,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = ret_value,
- },
- };
-
- intel_sdvo_debug_write(intel_sdvo, SDVO_CMD_SET_CONTROL_BUS_SWITCH,
- &target, 1);
- /* write the DDC switch command argument */
- intel_sdvo_write_byte(intel_sdvo, SDVO_I2C_ARG_0, target);
-
- out_buf[0] = SDVO_I2C_OPCODE;
- out_buf[1] = SDVO_CMD_SET_CONTROL_BUS_SWITCH;
- cmd_buf[0] = SDVO_I2C_CMD_STATUS;
- cmd_buf[1] = 0;
- ret_value[0] = 0;
- ret_value[1] = 0;
-
- ret = i2c_transfer(intel_sdvo->base.i2c_bus, msgs, 3);
- if (ret != 3) {
- /* failure in I2C transfer */
- DRM_DEBUG_KMS("I2c transfer returned %d\n", ret);
- return;
- }
- if (ret_value[0] != SDVO_CMD_STATUS_SUCCESS) {
- DRM_DEBUG_KMS("DDC switch command returns response %d\n",
- ret_value[0]);
- return;
- }
- return;
+ return intel_sdvo_write_cmd(intel_sdvo,
+ SDVO_CMD_SET_CONTROL_BUS_SWITCH,
+ &ddc_bus, 1);
}
static bool intel_sdvo_set_value(struct intel_sdvo *intel_sdvo, u8 cmd, const void *data, int len)
{
- if (!intel_sdvo_write_cmd(intel_sdvo, cmd, data, len))
- return false;
-
- return intel_sdvo_read_response(intel_sdvo, NULL, 0);
+ return intel_sdvo_write_cmd(intel_sdvo, cmd, data, len);
}
static bool
@@ -819,17 +793,13 @@ static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
mode->flags |= DRM_MODE_FLAG_PVSYNC;
}
-static bool intel_sdvo_get_supp_encode(struct intel_sdvo *intel_sdvo,
- struct intel_sdvo_encode *encode)
+static bool intel_sdvo_check_supp_encode(struct intel_sdvo *intel_sdvo)
{
- if (intel_sdvo_get_value(intel_sdvo,
- SDVO_CMD_GET_SUPP_ENCODE,
- encode, sizeof(*encode)))
- return true;
+ struct intel_sdvo_encode encode;
- /* non-support means DVI */
- memset(encode, 0, sizeof(*encode));
- return false;
+ return intel_sdvo_get_value(intel_sdvo,
+ SDVO_CMD_GET_SUPP_ENCODE,
+ &encode, sizeof(encode));
}
static bool intel_sdvo_set_encode(struct intel_sdvo *intel_sdvo,
@@ -874,115 +844,33 @@ static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo)
}
#endif
-static bool intel_sdvo_set_hdmi_buf(struct intel_sdvo *intel_sdvo,
- int index,
- uint8_t *data, int8_t size, uint8_t tx_rate)
-{
- uint8_t set_buf_index[2];
-
- set_buf_index[0] = index;
- set_buf_index[1] = 0;
-
- if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_INDEX,
- set_buf_index, 2))
- return false;
-
- for (; size > 0; size -= 8) {
- if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_DATA, data, 8))
- return false;
-
- data += 8;
- }
-
- return intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_TXRATE, &tx_rate, 1);
-}
-
-static uint8_t intel_sdvo_calc_hbuf_csum(uint8_t *data, uint8_t size)
-{
- uint8_t csum = 0;
- int i;
-
- for (i = 0; i < size; i++)
- csum += data[i];
-
- return 0x100 - csum;
-}
-
-#define DIP_TYPE_AVI 0x82
-#define DIP_VERSION_AVI 0x2
-#define DIP_LEN_AVI 13
-
-struct dip_infoframe {
- uint8_t type;
- uint8_t version;
- uint8_t len;
- uint8_t checksum;
- union {
- struct {
- /* Packet Byte #1 */
- uint8_t S:2;
- uint8_t B:2;
- uint8_t A:1;
- uint8_t Y:2;
- uint8_t rsvd1:1;
- /* Packet Byte #2 */
- uint8_t R:4;
- uint8_t M:2;
- uint8_t C:2;
- /* Packet Byte #3 */
- uint8_t SC:2;
- uint8_t Q:2;
- uint8_t EC:3;
- uint8_t ITC:1;
- /* Packet Byte #4 */
- uint8_t VIC:7;
- uint8_t rsvd2:1;
- /* Packet Byte #5 */
- uint8_t PR:4;
- uint8_t rsvd3:4;
- /* Packet Byte #6~13 */
- uint16_t top_bar_end;
- uint16_t bottom_bar_start;
- uint16_t left_bar_end;
- uint16_t right_bar_start;
- } avi;
- struct {
- /* Packet Byte #1 */
- uint8_t channel_count:3;
- uint8_t rsvd1:1;
- uint8_t coding_type:4;
- /* Packet Byte #2 */
- uint8_t sample_size:2; /* SS0, SS1 */
- uint8_t sample_frequency:3;
- uint8_t rsvd2:3;
- /* Packet Byte #3 */
- uint8_t coding_type_private:5;
- uint8_t rsvd3:3;
- /* Packet Byte #4 */
- uint8_t channel_allocation;
- /* Packet Byte #5 */
- uint8_t rsvd4:3;
- uint8_t level_shift:4;
- uint8_t downmix_inhibit:1;
- } audio;
- uint8_t payload[28];
- } __attribute__ ((packed)) u;
-} __attribute__((packed));
-
-static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo,
- struct drm_display_mode * mode)
+static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo)
{
struct dip_infoframe avi_if = {
.type = DIP_TYPE_AVI,
- .version = DIP_VERSION_AVI,
+ .ver = DIP_VERSION_AVI,
.len = DIP_LEN_AVI,
};
+ uint8_t tx_rate = SDVO_HBUF_TX_VSYNC;
+ uint8_t set_buf_index[2] = { 1, 0 };
+ uint64_t *data = (uint64_t *)&avi_if;
+ unsigned i;
+
+ intel_dip_infoframe_csum(&avi_if);
+
+ if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_INDEX,
+ set_buf_index, 2))
+ return false;
- avi_if.checksum = intel_sdvo_calc_hbuf_csum((uint8_t *)&avi_if,
- 4 + avi_if.len);
- return intel_sdvo_set_hdmi_buf(intel_sdvo, 1, (uint8_t *)&avi_if,
- 4 + avi_if.len,
- SDVO_HBUF_TX_VSYNC);
+ for (i = 0; i < sizeof(avi_if); i += 8) {
+ if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_DATA,
+ data, 8))
+ return false;
+ data++;
+ }
+
+ return intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_TXRATE,
+ &tx_rate, 1);
}
static bool intel_sdvo_set_tv_format(struct intel_sdvo *intel_sdvo)
@@ -1022,8 +910,6 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct intel_sdvo_dtd input_dtd;
-
/* Reset the input timing to the screen. Assume always input 0. */
if (!intel_sdvo_set_target_input(intel_sdvo))
return false;
@@ -1035,14 +921,12 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo,
return false;
if (!intel_sdvo_get_preferred_input_timing(intel_sdvo,
- &input_dtd))
+ &intel_sdvo->input_dtd))
return false;
- intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
- intel_sdvo->sdvo_flags = input_dtd.part2.sdvo_flags;
+ intel_sdvo_get_mode_from_dtd(adjusted_mode, &intel_sdvo->input_dtd);
drm_mode_set_crtcinfo(adjusted_mode, 0);
- mode->clock = adjusted_mode->clock;
return true;
}
@@ -1050,7 +934,8 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
+ int multiplier;
/* We need to construct preferred input timings based on our
* output timings. To do that, we have to set the output
@@ -1065,10 +950,8 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
mode,
adjusted_mode);
} else if (intel_sdvo->is_lvds) {
- drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode, 0);
-
if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo,
- intel_sdvo->sdvo_lvds_fixed_mode))
+ intel_sdvo->sdvo_lvds_fixed_mode))
return false;
(void) intel_sdvo_set_input_timings_for_mode(intel_sdvo,
@@ -1077,9 +960,10 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
}
/* Make the CRTC code factor in the SDVO pixel multiplier. The
- * SDVO device will be told of the multiplier during mode_set.
+ * SDVO device will factor out the multiplier during mode_set.
*/
- adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
+ multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode);
+ intel_mode_set_pixel_multiplier(adjusted_mode, multiplier);
return true;
}
@@ -1092,11 +976,12 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = encoder->crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
- u32 sdvox = 0;
- int sdvo_pixel_multiply, rate;
+ struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
+ u32 sdvox;
struct intel_sdvo_in_out_map in_out;
struct intel_sdvo_dtd input_dtd;
+ int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+ int rate;
if (!mode)
return;
@@ -1114,28 +999,23 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
SDVO_CMD_SET_IN_OUT_MAP,
&in_out, sizeof(in_out));
- if (intel_sdvo->is_hdmi) {
- if (!intel_sdvo_set_avi_infoframe(intel_sdvo, mode))
- return;
-
- sdvox |= SDVO_AUDIO_ENABLE;
- }
+ /* Set the output timings to the screen */
+ if (!intel_sdvo_set_target_output(intel_sdvo,
+ intel_sdvo->attached_output))
+ return;
/* We have tried to get input timing in mode_fixup, and filled into
- adjusted_mode */
- intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
- if (intel_sdvo->is_tv || intel_sdvo->is_lvds)
- input_dtd.part2.sdvo_flags = intel_sdvo->sdvo_flags;
-
- /* If it's a TV, we already set the output timing in mode_fixup.
- * Otherwise, the output timing is equal to the input timing.
+ * adjusted_mode.
*/
- if (!intel_sdvo->is_tv && !intel_sdvo->is_lvds) {
+ if (intel_sdvo->is_tv || intel_sdvo->is_lvds) {
+ input_dtd = intel_sdvo->input_dtd;
+ } else {
/* Set the output timing to the screen */
if (!intel_sdvo_set_target_output(intel_sdvo,
intel_sdvo->attached_output))
return;
+ intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
(void) intel_sdvo_set_output_timing(intel_sdvo, &input_dtd);
}
@@ -1143,31 +1023,18 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
if (!intel_sdvo_set_target_input(intel_sdvo))
return;
- if (intel_sdvo->is_tv) {
- if (!intel_sdvo_set_tv_format(intel_sdvo))
- return;
- }
+ if (intel_sdvo->is_hdmi &&
+ !intel_sdvo_set_avi_infoframe(intel_sdvo))
+ return;
- /* We would like to use intel_sdvo_create_preferred_input_timing() to
- * provide the device with a timing it can support, if it supports that
- * feature. However, presumably we would need to adjust the CRTC to
- * output the preferred timing, and we don't support that currently.
- */
-#if 0
- success = intel_sdvo_create_preferred_input_timing(encoder, clock,
- width, height);
- if (success) {
- struct intel_sdvo_dtd *input_dtd;
+ if (intel_sdvo->is_tv &&
+ !intel_sdvo_set_tv_format(intel_sdvo))
+ return;
- intel_sdvo_get_preferred_input_timing(encoder, &input_dtd);
- intel_sdvo_set_input_timing(encoder, &input_dtd);
- }
-#else
(void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd);
-#endif
- sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode);
- switch (sdvo_pixel_multiply) {
+ switch (pixel_multiplier) {
+ default:
case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break;
case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break;
case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break;
@@ -1176,14 +1043,14 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
return;
/* Set the SDVO control regs. */
- if (IS_I965G(dev)) {
- sdvox |= SDVO_BORDER_ENABLE;
+ if (INTEL_INFO(dev)->gen >= 4) {
+ sdvox = SDVO_BORDER_ENABLE;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
} else {
- sdvox |= I915_READ(intel_sdvo->sdvo_reg);
+ sdvox = I915_READ(intel_sdvo->sdvo_reg);
switch (intel_sdvo->sdvo_reg) {
case SDVOB:
sdvox &= SDVOB_PRESERVE_MASK;
@@ -1196,16 +1063,18 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
}
if (intel_crtc->pipe == 1)
sdvox |= SDVO_PIPE_B_SELECT;
+ if (intel_sdvo->has_audio)
+ sdvox |= SDVO_AUDIO_ENABLE;
- if (IS_I965G(dev)) {
+ if (INTEL_INFO(dev)->gen >= 4) {
/* done in crtc_mode_set as the dpll_md reg must be written early */
} else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
/* done in crtc_mode_set as it lives inside the dpll register */
} else {
- sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
+ sdvox |= (pixel_multiplier - 1) << SDVO_PORT_MULTIPLY_SHIFT;
}
- if (intel_sdvo->sdvo_flags & SDVO_NEED_TO_STALL)
+ if (input_dtd.part2.sdvo_flags & SDVO_NEED_TO_STALL)
sdvox |= SDVO_STALL_SELECT;
intel_sdvo_write_sdvox(intel_sdvo, sdvox);
}
@@ -1214,7 +1083,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
u32 temp;
@@ -1260,8 +1129,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
static int intel_sdvo_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
@@ -1285,7 +1153,38 @@ static int intel_sdvo_mode_valid(struct drm_connector *connector,
static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct intel_sdvo_caps *caps)
{
- return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_DEVICE_CAPS, caps, sizeof(*caps));
+ if (!intel_sdvo_get_value(intel_sdvo,
+ SDVO_CMD_GET_DEVICE_CAPS,
+ caps, sizeof(*caps)))
+ return false;
+
+ DRM_DEBUG_KMS("SDVO capabilities:\n"
+ " vendor_id: %d\n"
+ " device_id: %d\n"
+ " device_rev_id: %d\n"
+ " sdvo_version_major: %d\n"
+ " sdvo_version_minor: %d\n"
+ " sdvo_inputs_mask: %d\n"
+ " smooth_scaling: %d\n"
+ " sharp_scaling: %d\n"
+ " up_scaling: %d\n"
+ " down_scaling: %d\n"
+ " stall_support: %d\n"
+ " output_flags: %d\n",
+ caps->vendor_id,
+ caps->device_id,
+ caps->device_rev_id,
+ caps->sdvo_version_major,
+ caps->sdvo_version_minor,
+ caps->sdvo_inputs_mask,
+ caps->smooth_scaling,
+ caps->sharp_scaling,
+ caps->up_scaling,
+ caps->down_scaling,
+ caps->stall_support,
+ caps->output_flags);
+
+ return true;
}
/* No use! */
@@ -1389,22 +1288,33 @@ intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo)
return (caps > 1);
}
+static struct edid *
+intel_sdvo_get_edid(struct drm_connector *connector)
+{
+ struct intel_sdvo *sdvo = intel_attached_sdvo(connector);
+ return drm_get_edid(connector, &sdvo->ddc);
+}
+
static struct drm_connector *
intel_find_analog_connector(struct drm_device *dev)
{
struct drm_connector *connector;
- struct drm_encoder *encoder;
- struct intel_sdvo *intel_sdvo;
-
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- intel_sdvo = enc_to_intel_sdvo(encoder);
- if (intel_sdvo->base.type == INTEL_OUTPUT_ANALOG) {
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (encoder == intel_attached_encoder(connector))
+ struct intel_sdvo *encoder;
+
+ list_for_each_entry(encoder,
+ &dev->mode_config.encoder_list,
+ base.base.head) {
+ if (encoder->base.type == INTEL_OUTPUT_ANALOG) {
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list,
+ head) {
+ if (&encoder->base ==
+ intel_attached_encoder(connector))
return connector;
}
}
}
+
return NULL;
}
@@ -1424,64 +1334,72 @@ intel_analog_is_connected(struct drm_device *dev)
return true;
}
+/* Mac mini hack -- use the same DDC as the analog connector */
+static struct edid *
+intel_sdvo_get_analog_edid(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+
+ if (!intel_analog_is_connected(connector->dev))
+ return NULL;
+
+ return drm_get_edid(connector, &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
+}
+
enum drm_connector_status
intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
- struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
- enum drm_connector_status status = connector_status_connected;
- struct edid *edid = NULL;
+ struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
+ enum drm_connector_status status;
+ struct edid *edid;
- edid = drm_get_edid(connector, intel_sdvo->base.ddc_bus);
+ edid = intel_sdvo_get_edid(connector);
- /* This is only applied to SDVO cards with multiple outputs */
if (edid == NULL && intel_sdvo_multifunc_encoder(intel_sdvo)) {
- uint8_t saved_ddc, temp_ddc;
- saved_ddc = intel_sdvo->ddc_bus;
- temp_ddc = intel_sdvo->ddc_bus >> 1;
+ u8 ddc, saved_ddc = intel_sdvo->ddc_bus;
+
/*
* Don't use the 1 as the argument of DDC bus switch to get
* the EDID. It is used for SDVO SPD ROM.
*/
- while(temp_ddc > 1) {
- intel_sdvo->ddc_bus = temp_ddc;
- edid = drm_get_edid(connector, intel_sdvo->base.ddc_bus);
- if (edid) {
- /*
- * When we can get the EDID, maybe it is the
- * correct DDC bus. Update it.
- */
- intel_sdvo->ddc_bus = temp_ddc;
+ for (ddc = intel_sdvo->ddc_bus >> 1; ddc > 1; ddc >>= 1) {
+ intel_sdvo->ddc_bus = ddc;
+ edid = intel_sdvo_get_edid(connector);
+ if (edid)
break;
- }
- temp_ddc >>= 1;
}
+ /*
+ * If we found the EDID on the other bus,
+ * assume that is the correct DDC bus.
+ */
if (edid == NULL)
intel_sdvo->ddc_bus = saved_ddc;
}
- /* when there is no edid and no monitor is connected with VGA
- * port, try to use the CRT ddc to read the EDID for DVI-connector
+
+ /*
+ * When there is no edid and no monitor is connected with VGA
+ * port, try to use the CRT ddc to read the EDID for DVI-connector.
*/
- if (edid == NULL && intel_sdvo->analog_ddc_bus &&
- !intel_analog_is_connected(connector->dev))
- edid = drm_get_edid(connector, intel_sdvo->analog_ddc_bus);
+ if (edid == NULL)
+ edid = intel_sdvo_get_analog_edid(connector);
+ status = connector_status_unknown;
if (edid != NULL) {
- bool is_digital = !!(edid->input & DRM_EDID_INPUT_DIGITAL);
- bool need_digital = !!(intel_sdvo_connector->output_flag & SDVO_TMDS_MASK);
-
/* DDC bus is shared, match EDID to connector type */
- if (is_digital && need_digital)
+ if (edid->input & DRM_EDID_INPUT_DIGITAL) {
+ status = connector_status_connected;
intel_sdvo->is_hdmi = drm_detect_hdmi_monitor(edid);
- else if (is_digital != need_digital)
- status = connector_status_disconnected;
-
+ intel_sdvo->has_audio = drm_detect_monitor_audio(edid);
+ }
connector->display_info.raw_edid = NULL;
- } else
- status = connector_status_disconnected;
-
- kfree(edid);
+ kfree(edid);
+ }
+
+ 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_audio = intel_sdvo_connector->force_audio > 0;
+ }
return status;
}
@@ -1490,13 +1408,12 @@ static enum drm_connector_status
intel_sdvo_detect(struct drm_connector *connector, bool force)
{
uint16_t response;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
enum drm_connector_status ret;
if (!intel_sdvo_write_cmd(intel_sdvo,
- SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0))
+ SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0))
return connector_status_unknown;
if (intel_sdvo->is_tv) {
/* add 30ms delay when the output type is SDVO-TV */
@@ -1505,7 +1422,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
if (!intel_sdvo_read_response(intel_sdvo, &response, 2))
return connector_status_unknown;
- DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8);
+ DRM_DEBUG_KMS("SDVO response %d %d [%x]\n",
+ response & 0xff, response >> 8,
+ intel_sdvo_connector->output_flag);
if (response == 0)
return connector_status_disconnected;
@@ -1538,12 +1457,10 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
- int num_modes;
+ struct edid *edid;
/* set the bus switch and get the modes */
- num_modes = intel_ddc_get_modes(connector, intel_sdvo->base.ddc_bus);
+ edid = intel_sdvo_get_edid(connector);
/*
* Mac mini hack. On this device, the DVI-I connector shares one DDC
@@ -1551,12 +1468,14 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
* DDC fails, check to see if the analog output is disconnected, in
* which case we'll look there for the digital DDC data.
*/
- if (num_modes == 0 &&
- intel_sdvo->analog_ddc_bus &&
- !intel_analog_is_connected(connector->dev)) {
- /* Switch to the analog ddc bus and try that
- */
- (void) intel_ddc_get_modes(connector, intel_sdvo->analog_ddc_bus);
+ if (edid == NULL)
+ edid = intel_sdvo_get_analog_edid(connector);
+
+ if (edid != NULL) {
+ drm_mode_connector_update_edid_property(connector, edid);
+ drm_add_edid_modes(connector, edid);
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
}
}
@@ -1627,8 +1546,7 @@ struct drm_display_mode sdvo_tv_modes[] = {
static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct intel_sdvo_sdtv_resolution_request tv_res;
uint32_t reply = 0, format_map = 0;
int i;
@@ -1644,7 +1562,8 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
return;
BUILD_BUG_ON(sizeof(tv_res) != 3);
- if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
+ if (!intel_sdvo_write_cmd(intel_sdvo,
+ SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
&tv_res, sizeof(tv_res)))
return;
if (!intel_sdvo_read_response(intel_sdvo, &reply, 3))
@@ -1662,8 +1581,7 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct drm_display_mode *newmode;
@@ -1672,7 +1590,7 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
* Assume that the preferred modes are
* arranged in priority order.
*/
- intel_ddc_get_modes(connector, intel_sdvo->base.ddc_bus);
+ intel_ddc_get_modes(connector, intel_sdvo->i2c);
if (list_empty(&connector->probed_modes) == false)
goto end;
@@ -1693,6 +1611,10 @@ end:
if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
intel_sdvo->sdvo_lvds_fixed_mode =
drm_mode_duplicate(connector->dev, newmode);
+
+ drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode,
+ 0);
+
intel_sdvo->is_lvds = true;
break;
}
@@ -1775,8 +1697,7 @@ intel_sdvo_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
uint16_t temp_value;
uint8_t cmd;
@@ -1786,6 +1707,21 @@ intel_sdvo_set_property(struct drm_connector *connector,
if (ret)
return ret;
+ if (property == intel_sdvo_connector->force_audio_property) {
+ if (val == intel_sdvo_connector->force_audio)
+ return 0;
+
+ intel_sdvo_connector->force_audio = val;
+
+ if (val > 0 && intel_sdvo->has_audio)
+ return 0;
+ if (val < 0 && !intel_sdvo->has_audio)
+ return 0;
+
+ intel_sdvo->has_audio = val > 0;
+ goto done;
+ }
+
#define CHECK_PROPERTY(name, NAME) \
if (intel_sdvo_connector->name == property) { \
if (intel_sdvo_connector->cur_##name == temp_value) return 0; \
@@ -1879,9 +1815,8 @@ set_value:
done:
- if (encoder->crtc) {
- struct drm_crtc *crtc = encoder->crtc;
-
+ if (intel_sdvo->base.base.crtc) {
+ struct drm_crtc *crtc = intel_sdvo->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
crtc->y, crtc->fb);
}
@@ -1909,20 +1844,18 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
.get_modes = intel_sdvo_get_modes,
.mode_valid = intel_sdvo_mode_valid,
- .best_encoder = intel_attached_encoder,
+ .best_encoder = intel_best_encoder,
};
static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
{
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
-
- if (intel_sdvo->analog_ddc_bus)
- intel_i2c_destroy(intel_sdvo->analog_ddc_bus);
+ struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
if (intel_sdvo->sdvo_lvds_fixed_mode != NULL)
drm_mode_destroy(encoder->dev,
intel_sdvo->sdvo_lvds_fixed_mode);
+ i2c_del_adapter(&intel_sdvo->ddc);
intel_encoder_destroy(encoder);
}
@@ -1990,53 +1923,48 @@ intel_sdvo_select_ddc_bus(struct drm_i915_private *dev_priv,
intel_sdvo_guess_ddc_bus(sdvo);
}
-static bool
-intel_sdvo_get_digital_encoding_mode(struct intel_sdvo *intel_sdvo, int device)
+static void
+intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv,
+ struct intel_sdvo *sdvo, u32 reg)
{
- return intel_sdvo_set_target_output(intel_sdvo,
- device == 0 ? SDVO_OUTPUT_TMDS0 : SDVO_OUTPUT_TMDS1) &&
- intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE,
- &intel_sdvo->is_hdmi, 1);
-}
+ struct sdvo_device_mapping *mapping;
+ u8 pin, speed;
-static struct intel_sdvo *
-intel_sdvo_chan_to_intel_sdvo(struct intel_i2c_chan *chan)
-{
- struct drm_device *dev = chan->drm_dev;
- struct drm_encoder *encoder;
+ if (IS_SDVOB(reg))
+ mapping = &dev_priv->sdvo_mappings[0];
+ else
+ mapping = &dev_priv->sdvo_mappings[1];
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
- if (intel_sdvo->base.ddc_bus == &chan->adapter)
- return intel_sdvo;
+ pin = GMBUS_PORT_DPB;
+ speed = GMBUS_RATE_1MHZ >> 8;
+ if (mapping->initialized) {
+ pin = mapping->i2c_pin;
+ speed = mapping->i2c_speed;
}
- return NULL;
+ sdvo->i2c = &dev_priv->gmbus[pin].adapter;
+ intel_gmbus_set_speed(sdvo->i2c, speed);
+ intel_gmbus_force_bit(sdvo->i2c, true);
}
-static int intel_sdvo_master_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg msgs[], int num)
+static bool
+intel_sdvo_is_hdmi_connector(struct intel_sdvo *intel_sdvo, int device)
{
- struct intel_sdvo *intel_sdvo;
- struct i2c_algo_bit_data *algo_data;
- const struct i2c_algorithm *algo;
+ int is_hdmi;
- algo_data = (struct i2c_algo_bit_data *)i2c_adap->algo_data;
- intel_sdvo =
- intel_sdvo_chan_to_intel_sdvo((struct intel_i2c_chan *)
- (algo_data->data));
- if (intel_sdvo == NULL)
- return -EINVAL;
+ if (!intel_sdvo_check_supp_encode(intel_sdvo))
+ return false;
- algo = intel_sdvo->base.i2c_bus->algo;
+ if (!intel_sdvo_set_target_output(intel_sdvo,
+ device == 0 ? SDVO_OUTPUT_TMDS0 : SDVO_OUTPUT_TMDS1))
+ return false;
- intel_sdvo_set_control_bus_switch(intel_sdvo, intel_sdvo->ddc_bus);
- return algo->master_xfer(i2c_adap, msgs, num);
-}
+ is_hdmi = 0;
+ if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE, &is_hdmi, 1))
+ return false;
-static struct i2c_algorithm intel_sdvo_i2c_bit_algo = {
- .master_xfer = intel_sdvo_master_xfer,
-};
+ return !!is_hdmi;
+}
static u8
intel_sdvo_get_slave_addr(struct drm_device *dev, int sdvo_reg)
@@ -2076,26 +2004,44 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int sdvo_reg)
}
static void
-intel_sdvo_connector_init(struct drm_encoder *encoder,
- struct drm_connector *connector)
+intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
+ struct intel_sdvo *encoder)
{
- drm_connector_init(encoder->dev, connector, &intel_sdvo_connector_funcs,
- connector->connector_type);
+ drm_connector_init(encoder->base.base.dev,
+ &connector->base.base,
+ &intel_sdvo_connector_funcs,
+ connector->base.base.connector_type);
+
+ drm_connector_helper_add(&connector->base.base,
+ &intel_sdvo_connector_helper_funcs);
+
+ connector->base.base.interlace_allowed = 0;
+ connector->base.base.doublescan_allowed = 0;
+ connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
- drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
+ intel_connector_attach_encoder(&connector->base, &encoder->base);
+ drm_sysfs_connector_add(&connector->base.base);
+}
- connector->interlace_allowed = 0;
- connector->doublescan_allowed = 0;
- connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+static void
+intel_sdvo_add_hdmi_properties(struct intel_sdvo_connector *connector)
+{
+ struct drm_device *dev = connector->base.base.dev;
- drm_mode_connector_attach_encoder(connector, encoder);
- drm_sysfs_connector_add(connector);
+ connector->force_audio_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE, "force_audio", 2);
+ if (connector->force_audio_property) {
+ connector->force_audio_property->values[0] = -1;
+ connector->force_audio_property->values[1] = 1;
+ drm_connector_attach_property(&connector->base.base,
+ connector->force_audio_property, 0);
+ }
}
static bool
intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
{
- struct drm_encoder *encoder = &intel_sdvo->base.enc;
+ struct drm_encoder *encoder = &intel_sdvo->base.base;
struct drm_connector *connector;
struct intel_connector *intel_connector;
struct intel_sdvo_connector *intel_sdvo_connector;
@@ -2118,19 +2064,20 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
connector->connector_type = DRM_MODE_CONNECTOR_DVID;
- if (intel_sdvo_get_supp_encode(intel_sdvo, &intel_sdvo->encode)
- && intel_sdvo_get_digital_encoding_mode(intel_sdvo, device)
- && intel_sdvo->is_hdmi) {
+ if (intel_sdvo_is_hdmi_connector(intel_sdvo, device)) {
/* enable hdmi encoding mode if supported */
intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
intel_sdvo_set_colorimetry(intel_sdvo,
SDVO_COLORIMETRY_RGB256);
connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ intel_sdvo->is_hdmi = true;
}
intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
(1 << INTEL_ANALOG_CLONE_BIT));
- intel_sdvo_connector_init(encoder, connector);
+ intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
+
+ intel_sdvo_add_hdmi_properties(intel_sdvo_connector);
return true;
}
@@ -2138,36 +2085,36 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
static bool
intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
{
- struct drm_encoder *encoder = &intel_sdvo->base.enc;
- struct drm_connector *connector;
- struct intel_connector *intel_connector;
- struct intel_sdvo_connector *intel_sdvo_connector;
+ struct drm_encoder *encoder = &intel_sdvo->base.base;
+ struct drm_connector *connector;
+ struct intel_connector *intel_connector;
+ struct intel_sdvo_connector *intel_sdvo_connector;
intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
if (!intel_sdvo_connector)
return false;
intel_connector = &intel_sdvo_connector->base;
- connector = &intel_connector->base;
- encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
- connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
+ connector = &intel_connector->base;
+ encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
+ connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
- intel_sdvo->controlled_output |= type;
- intel_sdvo_connector->output_flag = type;
+ intel_sdvo->controlled_output |= type;
+ intel_sdvo_connector->output_flag = type;
- intel_sdvo->is_tv = true;
- intel_sdvo->base.needs_tv_clock = true;
- intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
+ intel_sdvo->is_tv = true;
+ intel_sdvo->base.needs_tv_clock = true;
+ intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
- intel_sdvo_connector_init(encoder, connector);
+ intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
- if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type))
+ if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type))
goto err;
- if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
+ if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
goto err;
- return true;
+ return true;
err:
intel_sdvo_destroy(connector);
@@ -2177,43 +2124,44 @@ err:
static bool
intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
{
- struct drm_encoder *encoder = &intel_sdvo->base.enc;
- struct drm_connector *connector;
- struct intel_connector *intel_connector;
- struct intel_sdvo_connector *intel_sdvo_connector;
+ struct drm_encoder *encoder = &intel_sdvo->base.base;
+ struct drm_connector *connector;
+ struct intel_connector *intel_connector;
+ struct intel_sdvo_connector *intel_sdvo_connector;
intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
if (!intel_sdvo_connector)
return false;
intel_connector = &intel_sdvo_connector->base;
- connector = &intel_connector->base;
+ connector = &intel_connector->base;
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
- encoder->encoder_type = DRM_MODE_ENCODER_DAC;
- connector->connector_type = DRM_MODE_CONNECTOR_VGA;
-
- if (device == 0) {
- intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB0;
- intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB0;
- } else if (device == 1) {
- intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB1;
- intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1;
- }
-
- intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
+ encoder->encoder_type = DRM_MODE_ENCODER_DAC;
+ connector->connector_type = DRM_MODE_CONNECTOR_VGA;
+
+ if (device == 0) {
+ intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB0;
+ intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB0;
+ } else if (device == 1) {
+ intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB1;
+ intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1;
+ }
+
+ intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
(1 << INTEL_ANALOG_CLONE_BIT));
- intel_sdvo_connector_init(encoder, connector);
- return true;
+ intel_sdvo_connector_init(intel_sdvo_connector,
+ intel_sdvo);
+ return true;
}
static bool
intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
{
- struct drm_encoder *encoder = &intel_sdvo->base.enc;
- struct drm_connector *connector;
- struct intel_connector *intel_connector;
- struct intel_sdvo_connector *intel_sdvo_connector;
+ struct drm_encoder *encoder = &intel_sdvo->base.base;
+ struct drm_connector *connector;
+ struct intel_connector *intel_connector;
+ struct intel_sdvo_connector *intel_sdvo_connector;
intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
if (!intel_sdvo_connector)
@@ -2221,22 +2169,22 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
intel_connector = &intel_sdvo_connector->base;
connector = &intel_connector->base;
- encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
- connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
-
- if (device == 0) {
- intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS0;
- intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS0;
- } else if (device == 1) {
- intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS1;
- intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1;
- }
-
- intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) |
+ encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
+ connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
+
+ if (device == 0) {
+ intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS0;
+ intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS0;
+ } else if (device == 1) {
+ intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS1;
+ intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1;
+ }
+
+ intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) |
(1 << INTEL_SDVO_LVDS_CLONE_BIT));
- intel_sdvo_connector_init(encoder, connector);
- if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
+ intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
+ if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
goto err;
return true;
@@ -2307,7 +2255,7 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
int type)
{
- struct drm_device *dev = intel_sdvo->base.enc.dev;
+ struct drm_device *dev = intel_sdvo->base.base.dev;
struct intel_sdvo_tv_format format;
uint32_t format_map, i;
@@ -2373,7 +2321,7 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
struct intel_sdvo_enhancements_reply enhancements)
{
- struct drm_device *dev = intel_sdvo->base.enc.dev;
+ struct drm_device *dev = intel_sdvo->base.base.dev;
struct drm_connector *connector = &intel_sdvo_connector->base.base;
uint16_t response, data_value[2];
@@ -2502,7 +2450,7 @@ intel_sdvo_create_enhance_property_lvds(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
struct intel_sdvo_enhancements_reply enhancements)
{
- struct drm_device *dev = intel_sdvo->base.enc.dev;
+ struct drm_device *dev = intel_sdvo->base.base.dev;
struct drm_connector *connector = &intel_sdvo_connector->base.base;
uint16_t response, data_value[2];
@@ -2535,7 +2483,43 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply);
else
return true;
+}
+
+static int intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct intel_sdvo *sdvo = adapter->algo_data;
+ if (!intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
+ return -EIO;
+
+ return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num);
+}
+
+static u32 intel_sdvo_ddc_proxy_func(struct i2c_adapter *adapter)
+{
+ struct intel_sdvo *sdvo = adapter->algo_data;
+ return sdvo->i2c->algo->functionality(sdvo->i2c);
+}
+
+static const struct i2c_algorithm intel_sdvo_ddc_proxy = {
+ .master_xfer = intel_sdvo_ddc_proxy_xfer,
+ .functionality = intel_sdvo_ddc_proxy_func
+};
+
+static bool
+intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo,
+ struct drm_device *dev)
+{
+ sdvo->ddc.owner = THIS_MODULE;
+ sdvo->ddc.class = I2C_CLASS_DDC;
+ snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy");
+ sdvo->ddc.dev.parent = &dev->pdev->dev;
+ sdvo->ddc.algo_data = sdvo;
+ sdvo->ddc.algo = &intel_sdvo_ddc_proxy;
+
+ return i2c_add_adapter(&sdvo->ddc) == 0;
}
bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg)
@@ -2543,95 +2527,66 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *intel_encoder;
struct intel_sdvo *intel_sdvo;
- u8 ch[0x40];
int i;
- u32 i2c_reg, ddc_reg, analog_ddc_reg;
intel_sdvo = kzalloc(sizeof(struct intel_sdvo), GFP_KERNEL);
if (!intel_sdvo)
return false;
+ if (!intel_sdvo_init_ddc_proxy(intel_sdvo, dev)) {
+ kfree(intel_sdvo);
+ return false;
+ }
+
intel_sdvo->sdvo_reg = sdvo_reg;
intel_encoder = &intel_sdvo->base;
intel_encoder->type = INTEL_OUTPUT_SDVO;
+ /* encoder type will be decided later */
+ drm_encoder_init(dev, &intel_encoder->base, &intel_sdvo_enc_funcs, 0);
- if (HAS_PCH_SPLIT(dev)) {
- i2c_reg = PCH_GPIOE;
- ddc_reg = PCH_GPIOE;
- analog_ddc_reg = PCH_GPIOA;
- } else {
- i2c_reg = GPIOE;
- ddc_reg = GPIOE;
- analog_ddc_reg = GPIOA;
- }
-
- /* setup the DDC bus. */
- if (IS_SDVOB(sdvo_reg))
- intel_encoder->i2c_bus = intel_i2c_create(dev, i2c_reg, "SDVOCTRL_E for SDVOB");
- else
- intel_encoder->i2c_bus = intel_i2c_create(dev, i2c_reg, "SDVOCTRL_E for SDVOC");
-
- if (!intel_encoder->i2c_bus)
- goto err_inteloutput;
-
- intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, sdvo_reg);
-
- /* Save the bit-banging i2c functionality for use by the DDC wrapper */
- intel_sdvo_i2c_bit_algo.functionality = intel_encoder->i2c_bus->algo->functionality;
+ intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, sdvo_reg) >> 1;
+ intel_sdvo_select_i2c_bus(dev_priv, intel_sdvo, sdvo_reg);
/* Read the regs to test if we can talk to the device */
for (i = 0; i < 0x40; i++) {
- if (!intel_sdvo_read_byte(intel_sdvo, i, &ch[i])) {
+ u8 byte;
+
+ if (!intel_sdvo_read_byte(intel_sdvo, i, &byte)) {
DRM_DEBUG_KMS("No SDVO device found on SDVO%c\n",
IS_SDVOB(sdvo_reg) ? 'B' : 'C');
- goto err_i2c;
+ goto err;
}
}
- /* setup the DDC bus. */
- if (IS_SDVOB(sdvo_reg)) {
- intel_encoder->ddc_bus = intel_i2c_create(dev, ddc_reg, "SDVOB DDC BUS");
- intel_sdvo->analog_ddc_bus = intel_i2c_create(dev, analog_ddc_reg,
- "SDVOB/VGA DDC BUS");
+ if (IS_SDVOB(sdvo_reg))
dev_priv->hotplug_supported_mask |= SDVOB_HOTPLUG_INT_STATUS;
- } else {
- intel_encoder->ddc_bus = intel_i2c_create(dev, ddc_reg, "SDVOC DDC BUS");
- intel_sdvo->analog_ddc_bus = intel_i2c_create(dev, analog_ddc_reg,
- "SDVOC/VGA DDC BUS");
+ else
dev_priv->hotplug_supported_mask |= SDVOC_HOTPLUG_INT_STATUS;
- }
- if (intel_encoder->ddc_bus == NULL || intel_sdvo->analog_ddc_bus == NULL)
- goto err_i2c;
- /* Wrap with our custom algo which switches to DDC mode */
- intel_encoder->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
-
- /* encoder type will be decided later */
- drm_encoder_init(dev, &intel_encoder->enc, &intel_sdvo_enc_funcs, 0);
- drm_encoder_helper_add(&intel_encoder->enc, &intel_sdvo_helper_funcs);
+ drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs);
/* In default case sdvo lvds is false */
if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
- goto err_enc;
+ goto err;
if (intel_sdvo_output_setup(intel_sdvo,
intel_sdvo->caps.output_flags) != true) {
DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n",
IS_SDVOB(sdvo_reg) ? 'B' : 'C');
- goto err_enc;
+ goto err;
}
intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg);
/* Set the input timing to the screen. Assume always input 0. */
if (!intel_sdvo_set_target_input(intel_sdvo))
- goto err_enc;
+ goto err;
if (!intel_sdvo_get_input_pixel_clock_range(intel_sdvo,
&intel_sdvo->pixel_clock_min,
&intel_sdvo->pixel_clock_max))
- goto err_enc;
+ goto err;
DRM_DEBUG_KMS("%s device VID/DID: %02X:%02X.%02X, "
"clock range %dMHz - %dMHz, "
@@ -2651,16 +2606,9 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg)
(SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
return true;
-err_enc:
- drm_encoder_cleanup(&intel_encoder->enc);
-err_i2c:
- if (intel_sdvo->analog_ddc_bus != NULL)
- intel_i2c_destroy(intel_sdvo->analog_ddc_bus);
- if (intel_encoder->ddc_bus != NULL)
- intel_i2c_destroy(intel_encoder->ddc_bus);
- if (intel_encoder->i2c_bus != NULL)
- intel_i2c_destroy(intel_encoder->i2c_bus);
-err_inteloutput:
+err:
+ drm_encoder_cleanup(&intel_encoder->base);
+ i2c_del_adapter(&intel_sdvo->ddc);
kfree(intel_sdvo);
return false;
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 4a117e318a73..2f7681989316 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -48,7 +48,7 @@ struct intel_tv {
struct intel_encoder base;
int type;
- char *tv_format;
+ const char *tv_format;
int margin[4];
u32 save_TV_H_CTL_1;
u32 save_TV_H_CTL_2;
@@ -350,7 +350,7 @@ static const struct video_levels component_levels = {
struct tv_mode {
- char *name;
+ const char *name;
int clock;
int refresh; /* in millihertz (for precision) */
u32 oversample;
@@ -900,7 +900,14 @@ static const struct tv_mode tv_modes[] = {
static struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder)
{
- return container_of(enc_to_intel_encoder(encoder), struct intel_tv, base);
+ return container_of(encoder, struct intel_tv, base.base);
+}
+
+static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
+{
+ return container_of(intel_attached_encoder(connector),
+ struct intel_tv,
+ base);
}
static void
@@ -922,7 +929,7 @@ intel_tv_dpms(struct drm_encoder *encoder, int mode)
}
static const struct tv_mode *
-intel_tv_mode_lookup (char *tv_format)
+intel_tv_mode_lookup(const char *tv_format)
{
int i;
@@ -936,22 +943,23 @@ intel_tv_mode_lookup (char *tv_format)
}
static const struct tv_mode *
-intel_tv_mode_find (struct intel_tv *intel_tv)
+intel_tv_mode_find(struct intel_tv *intel_tv)
{
return intel_tv_mode_lookup(intel_tv->tv_format);
}
static enum drm_mode_status
-intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode)
+intel_tv_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
+ struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
/* Ensure TV refresh is close to desired refresh */
if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000)
< 1000)
return MODE_OK;
+
return MODE_CLOCK_RANGE;
}
@@ -1131,7 +1139,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
color_conversion->av);
}
- if (IS_I965G(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
I915_WRITE(TV_CLR_KNOBS, 0x00404000);
else
I915_WRITE(TV_CLR_KNOBS, 0x00606000);
@@ -1157,12 +1165,12 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
/* Wait for vblank for the disable to take effect */
- if (!IS_I9XX(dev))
+ if (IS_GEN2(dev))
intel_wait_for_vblank(dev, intel_crtc->pipe);
- I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
+ I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE);
/* Wait for vblank for the disable to take effect. */
- intel_wait_for_vblank(dev, intel_crtc->pipe);
+ intel_wait_for_pipe_off(dev, intel_crtc->pipe);
/* Filter ctl must be set before TV_WIN_SIZE */
I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
@@ -1196,7 +1204,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
for (i = 0; i < 43; i++)
I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
- I915_WRITE(TV_DAC, 0);
+ I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE);
I915_WRITE(TV_CTL, tv_ctl);
}
@@ -1228,15 +1236,13 @@ static const struct drm_display_mode reported_modes[] = {
static int
intel_tv_detect_type (struct intel_tv *intel_tv)
{
- struct drm_encoder *encoder = &intel_tv->base.enc;
+ struct drm_encoder *encoder = &intel_tv->base.base;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
u32 tv_ctl, save_tv_ctl;
u32 tv_dac, save_tv_dac;
- int type = DRM_MODE_CONNECTOR_Unknown;
-
- tv_dac = I915_READ(TV_DAC);
+ int type;
/* Disable TV interrupts around load detect or we'll recurse */
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
@@ -1244,19 +1250,14 @@ intel_tv_detect_type (struct intel_tv *intel_tv)
PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
- /*
- * Detect TV by polling)
- */
- save_tv_dac = tv_dac;
- tv_ctl = I915_READ(TV_CTL);
- save_tv_ctl = tv_ctl;
- tv_ctl &= ~TV_ENC_ENABLE;
- tv_ctl &= ~TV_TEST_MODE_MASK;
+ save_tv_dac = tv_dac = I915_READ(TV_DAC);
+ save_tv_ctl = tv_ctl = I915_READ(TV_CTL);
+
+ /* Poll for TV detection */
+ tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK);
tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
- tv_dac &= ~TVDAC_SENSE_MASK;
- tv_dac &= ~DAC_A_MASK;
- tv_dac &= ~DAC_B_MASK;
- tv_dac &= ~DAC_C_MASK;
+
+ tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK);
tv_dac |= (TVDAC_STATE_CHG_EN |
TVDAC_A_SENSE_CTL |
TVDAC_B_SENSE_CTL |
@@ -1265,37 +1266,40 @@ intel_tv_detect_type (struct intel_tv *intel_tv)
DAC_A_0_7_V |
DAC_B_0_7_V |
DAC_C_0_7_V);
+
I915_WRITE(TV_CTL, tv_ctl);
I915_WRITE(TV_DAC, tv_dac);
POSTING_READ(TV_DAC);
- msleep(20);
- tv_dac = I915_READ(TV_DAC);
- I915_WRITE(TV_DAC, save_tv_dac);
- I915_WRITE(TV_CTL, save_tv_ctl);
- POSTING_READ(TV_CTL);
- msleep(20);
+ intel_wait_for_vblank(intel_tv->base.base.dev,
+ to_intel_crtc(intel_tv->base.base.crtc)->pipe);
- /*
- * A B C
- * 0 1 1 Composite
- * 1 0 X svideo
- * 0 0 0 Component
- */
- if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
- DRM_DEBUG_KMS("Detected Composite TV connection\n");
- type = DRM_MODE_CONNECTOR_Composite;
- } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
- DRM_DEBUG_KMS("Detected S-Video TV connection\n");
- type = DRM_MODE_CONNECTOR_SVIDEO;
- } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
- DRM_DEBUG_KMS("Detected Component TV connection\n");
- type = DRM_MODE_CONNECTOR_Component;
- } else {
- DRM_DEBUG_KMS("No TV connection detected\n");
- type = -1;
+ type = -1;
+ if (wait_for((tv_dac = I915_READ(TV_DAC)) & TVDAC_STATE_CHG, 20) == 0) {
+ DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac);
+ /*
+ * A B C
+ * 0 1 1 Composite
+ * 1 0 X svideo
+ * 0 0 0 Component
+ */
+ if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+ DRM_DEBUG_KMS("Detected Composite TV connection\n");
+ type = DRM_MODE_CONNECTOR_Composite;
+ } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+ DRM_DEBUG_KMS("Detected S-Video TV connection\n");
+ type = DRM_MODE_CONNECTOR_SVIDEO;
+ } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+ DRM_DEBUG_KMS("Detected Component TV connection\n");
+ type = DRM_MODE_CONNECTOR_Component;
+ } else {
+ DRM_DEBUG_KMS("Unrecognised TV connection\n");
+ }
}
+ I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
+ I915_WRITE(TV_CTL, save_tv_ctl);
+
/* Restore interrupt config */
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
i915_enable_pipestat(dev_priv, 0, PIPE_HOTPLUG_INTERRUPT_ENABLE |
@@ -1311,8 +1315,7 @@ intel_tv_detect_type (struct intel_tv *intel_tv)
*/
static void intel_tv_find_better_format(struct drm_connector *connector)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
+ struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
int i;
@@ -1344,14 +1347,13 @@ static enum drm_connector_status
intel_tv_detect(struct drm_connector *connector, bool force)
{
struct drm_display_mode mode;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
+ struct intel_tv *intel_tv = intel_attached_tv(connector);
int type;
mode = reported_modes[0];
drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V);
- if (encoder->crtc && encoder->crtc->enabled) {
+ if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) {
type = intel_tv_detect_type(intel_tv);
} else if (force) {
struct drm_crtc *crtc;
@@ -1375,11 +1377,10 @@ intel_tv_detect(struct drm_connector *connector, bool force)
return connector_status_connected;
}
-static struct input_res {
- char *name;
+static const struct input_res {
+ const char *name;
int w, h;
-} input_res_table[] =
-{
+} input_res_table[] = {
{"640x480", 640, 480},
{"800x600", 800, 600},
{"1024x768", 1024, 768},
@@ -1396,8 +1397,7 @@ static void
intel_tv_chose_preferred_modes(struct drm_connector *connector,
struct drm_display_mode *mode_ptr)
{
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
+ struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
@@ -1422,15 +1422,14 @@ static int
intel_tv_get_modes(struct drm_connector *connector)
{
struct drm_display_mode *mode_ptr;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
+ struct intel_tv *intel_tv = intel_attached_tv(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
int j, count = 0;
u64 tmp;
for (j = 0; j < ARRAY_SIZE(input_res_table);
j++) {
- struct input_res *input = &input_res_table[j];
+ const struct input_res *input = &input_res_table[j];
unsigned int hactive_s = input->w;
unsigned int vactive_s = input->h;
@@ -1488,9 +1487,8 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
uint64_t val)
{
struct drm_device *dev = connector->dev;
- struct drm_encoder *encoder = intel_attached_encoder(connector);
- struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
- struct drm_crtc *crtc = encoder->crtc;
+ struct intel_tv *intel_tv = intel_attached_tv(connector);
+ struct drm_crtc *crtc = intel_tv->base.base.crtc;
int ret = 0;
bool changed = false;
@@ -1555,7 +1553,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = {
static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
.mode_valid = intel_tv_mode_valid,
.get_modes = intel_tv_get_modes,
- .best_encoder = intel_attached_encoder,
+ .best_encoder = intel_best_encoder,
};
static const struct drm_encoder_funcs intel_tv_enc_funcs = {
@@ -1607,7 +1605,7 @@ intel_tv_init(struct drm_device *dev)
struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector;
u32 tv_dac_on, tv_dac_off, save_tv_dac;
- char **tv_format_names;
+ char *tv_format_names[ARRAY_SIZE(tv_modes)];
int i, initial_mode = 0;
if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
@@ -1661,15 +1659,15 @@ intel_tv_init(struct drm_device *dev)
drm_connector_init(dev, connector, &intel_tv_connector_funcs,
DRM_MODE_CONNECTOR_SVIDEO);
- drm_encoder_init(dev, &intel_encoder->enc, &intel_tv_enc_funcs,
+ drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
DRM_MODE_ENCODER_TVDAC);
- drm_mode_connector_attach_encoder(&intel_connector->base, &intel_encoder->enc);
+ intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_TVOUT;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT);
- intel_encoder->enc.possible_crtcs = ((1 << 0) | (1 << 1));
- intel_encoder->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
+ intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
+ intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
/* BIOS margin values */
@@ -1678,21 +1676,19 @@ intel_tv_init(struct drm_device *dev)
intel_tv->margin[TV_MARGIN_RIGHT] = 46;
intel_tv->margin[TV_MARGIN_BOTTOM] = 37;
- intel_tv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL);
+ intel_tv->tv_format = tv_modes[initial_mode].name;
- drm_encoder_helper_add(&intel_encoder->enc, &intel_tv_helper_funcs);
+ drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs);
drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
/* Create TV properties then attach current values */
- tv_format_names = kmalloc(sizeof(char *) * ARRAY_SIZE(tv_modes),
- GFP_KERNEL);
- if (!tv_format_names)
- goto out;
for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
- tv_format_names[i] = tv_modes[i].name;
- drm_mode_create_tv_properties(dev, ARRAY_SIZE(tv_modes), tv_format_names);
+ tv_format_names[i] = (char *)tv_modes[i].name;
+ drm_mode_create_tv_properties(dev,
+ ARRAY_SIZE(tv_modes),
+ tv_format_names);
drm_connector_attach_property(connector, dev->mode_config.tv_mode_property,
initial_mode);
@@ -1708,6 +1704,5 @@ intel_tv_init(struct drm_device *dev)
drm_connector_attach_property(connector,
dev->mode_config.tv_bottom_margin_property,
intel_tv->margin[TV_MARGIN_BOTTOM]);
-out:
drm_sysfs_connector_add(connector);
}
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
index ac64f0b0392e..0aaf5f67a436 100644
--- a/drivers/gpu/drm/mga/mga_drv.c
+++ b/drivers/gpu/drm/mga/mga_drv.c
@@ -60,8 +60,6 @@ static struct drm_driver driver = {
.irq_uninstall = mga_driver_irq_uninstall,
.irq_handler = mga_driver_irq_handler,
.reclaim_buffers = drm_core_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = mga_ioctls,
.dma_ioctl = mga_dma_buffers,
.fops = {
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index d2d28048efb2..72730e9ca06c 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -10,6 +10,7 @@ config DRM_NOUVEAU
select FB
select FRAMEBUFFER_CONSOLE if !EMBEDDED
select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
+ select ACPI_VIDEO if ACPI
help
Choose this option for open-source nVidia support.
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index e9b06e4ef2a2..23fa82d667d6 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -9,7 +9,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
- nouveau_dp.o \
+ nouveau_dp.o nouveau_ramht.o \
+ nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.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 \
@@ -23,7 +24,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
nv10_gpio.o nv50_gpio.o \
- nv50_calc.o
+ nv50_calc.o \
+ nv04_pm.o nv50_pm.o nva3_pm.o
nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index c17a055ee3e5..119152606e4c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -292,6 +292,6 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
if (ret < 0)
return ret;
- nv_connector->edid = edid;
+ nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 974b0f8ae048..5f21030a293b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -43,9 +43,6 @@
#define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg)
#define LOG_OLD_VALUE(x)
-#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
-#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
-
struct init_exec {
bool execute;
bool repeat;
@@ -272,12 +269,6 @@ struct init_tbl_entry {
int (*handler)(struct nvbios *, uint16_t, struct init_exec *);
};
-struct bit_entry {
- uint8_t id[2];
- uint16_t length;
- uint16_t offset;
-};
-
static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
#define MACRO_INDEX_SIZE 2
@@ -1231,7 +1222,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
return 3;
}
- if (cond & 1)
+ if (!(cond & 1))
iexec->execute = false;
}
break;
@@ -2167,11 +2158,11 @@ peek_fb(struct drm_device *dev, struct io_mapping *fb,
if (off < pci_resource_len(dev->pdev, 1)) {
uint8_t __iomem *p =
- io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0);
+ io_mapping_map_atomic_wc(fb, off & PAGE_MASK);
val = ioread32(p + (off & ~PAGE_MASK));
- io_mapping_unmap_atomic(p, KM_USER0);
+ io_mapping_unmap_atomic(p);
}
return val;
@@ -2183,12 +2174,12 @@ poke_fb(struct drm_device *dev, struct io_mapping *fb,
{
if (off < pci_resource_len(dev->pdev, 1)) {
uint8_t __iomem *p =
- io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0);
+ io_mapping_map_atomic_wc(fb, off & PAGE_MASK);
iowrite32(val, p + (off & ~PAGE_MASK));
wmb();
- io_mapping_unmap_atomic(p, KM_USER0);
+ io_mapping_unmap_atomic(p);
}
}
@@ -4675,6 +4666,92 @@ int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, i
return 0;
}
+struct pll_mapping {
+ u8 type;
+ u32 reg;
+};
+
+static struct pll_mapping nv04_pll_mapping[] = {
+ { PLL_CORE , NV_PRAMDAC_NVPLL_COEFF },
+ { PLL_MEMORY, NV_PRAMDAC_MPLL_COEFF },
+ { PLL_VPLL0 , NV_PRAMDAC_VPLL_COEFF },
+ { PLL_VPLL1 , NV_RAMDAC_VPLL2 },
+ {}
+};
+
+static struct pll_mapping nv40_pll_mapping[] = {
+ { PLL_CORE , 0x004000 },
+ { PLL_MEMORY, 0x004020 },
+ { PLL_VPLL0 , NV_PRAMDAC_VPLL_COEFF },
+ { PLL_VPLL1 , NV_RAMDAC_VPLL2 },
+ {}
+};
+
+static struct pll_mapping nv50_pll_mapping[] = {
+ { PLL_CORE , 0x004028 },
+ { PLL_SHADER, 0x004020 },
+ { PLL_UNK03 , 0x004000 },
+ { PLL_MEMORY, 0x004008 },
+ { PLL_UNK40 , 0x00e810 },
+ { PLL_UNK41 , 0x00e818 },
+ { PLL_UNK42 , 0x00e824 },
+ { PLL_VPLL0 , 0x614100 },
+ { PLL_VPLL1 , 0x614900 },
+ {}
+};
+
+static struct pll_mapping nv84_pll_mapping[] = {
+ { PLL_CORE , 0x004028 },
+ { PLL_SHADER, 0x004020 },
+ { PLL_MEMORY, 0x004008 },
+ { PLL_UNK05 , 0x004030 },
+ { PLL_UNK41 , 0x00e818 },
+ { PLL_VPLL0 , 0x614100 },
+ { PLL_VPLL1 , 0x614900 },
+ {}
+};
+
+u32
+get_pll_register(struct drm_device *dev, enum pll_types type)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct pll_mapping *map;
+ int i;
+
+ if (dev_priv->card_type < NV_40)
+ map = nv04_pll_mapping;
+ else
+ if (dev_priv->card_type < NV_50)
+ map = nv40_pll_mapping;
+ else {
+ u8 *plim = &bios->data[bios->pll_limit_tbl_ptr];
+
+ if (plim[0] >= 0x30) {
+ u8 *entry = plim + plim[1];
+ for (i = 0; i < plim[3]; i++, entry += plim[2]) {
+ if (entry[0] == type)
+ return ROM32(entry[3]);
+ }
+
+ return 0;
+ }
+
+ if (dev_priv->chipset == 0x50)
+ map = nv50_pll_mapping;
+ else
+ map = nv84_pll_mapping;
+ }
+
+ while (map->reg) {
+ if (map->type == type)
+ return map->reg;
+ map++;
+ }
+
+ return 0;
+}
+
int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim)
{
/*
@@ -4750,6 +4827,17 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
/* initialize all members to zero */
memset(pll_lim, 0, sizeof(struct pll_lims));
+ /* if we were passed a type rather than a register, figure
+ * out the register and store it
+ */
+ if (limit_match > PLL_MAX)
+ pll_lim->reg = limit_match;
+ else {
+ pll_lim->reg = get_pll_register(dev, limit_match);
+ if (!pll_lim->reg)
+ return -ENOENT;
+ }
+
if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) {
uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex];
@@ -4785,7 +4873,6 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
pll_lim->max_usable_log2p = 0x6;
} else if (pll_lim_ver == 0x20 || pll_lim_ver == 0x21) {
uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen;
- uint32_t reg = 0; /* default match */
uint8_t *pll_rec;
int i;
@@ -4797,37 +4884,22 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
NV_WARN(dev, "Default PLL limit entry has non-zero "
"register field\n");
- if (limit_match > MAX_PLL_TYPES)
- /* we've been passed a reg as the match */
- reg = limit_match;
- else /* limit match is a pll type */
- for (i = 1; i < entries && !reg; i++) {
- uint32_t cmpreg = ROM32(bios->data[plloffs + recordlen * i]);
-
- if (limit_match == NVPLL &&
- (cmpreg == NV_PRAMDAC_NVPLL_COEFF || cmpreg == 0x4000))
- reg = cmpreg;
- if (limit_match == MPLL &&
- (cmpreg == NV_PRAMDAC_MPLL_COEFF || cmpreg == 0x4020))
- reg = cmpreg;
- if (limit_match == VPLL1 &&
- (cmpreg == NV_PRAMDAC_VPLL_COEFF || cmpreg == 0x4010))
- reg = cmpreg;
- if (limit_match == VPLL2 &&
- (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018))
- reg = cmpreg;
- }
-
for (i = 1; i < entries; i++)
- if (ROM32(bios->data[plloffs + recordlen * i]) == reg) {
+ if (ROM32(bios->data[plloffs + recordlen * i]) == pll_lim->reg) {
pllindex = i;
break;
}
+ if ((dev_priv->card_type >= NV_50) && (pllindex == 0)) {
+ NV_ERROR(dev, "Register 0x%08x not found in PLL "
+ "limits table", pll_lim->reg);
+ return -ENOENT;
+ }
+
pll_rec = &bios->data[plloffs + recordlen * pllindex];
BIOSLOG(bios, "Loading PLL limits for reg 0x%08x\n",
- pllindex ? reg : 0);
+ pllindex ? pll_lim->reg : 0);
/*
* Frequencies are stored in tables in MHz, kHz are more
@@ -4877,8 +4949,8 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
if (cv == 0x51 && !pll_lim->refclk) {
uint32_t sel_clk = bios_rd32(bios, NV_PRAMDAC_SEL_CLK);
- if (((limit_match == NV_PRAMDAC_VPLL_COEFF || limit_match == VPLL1) && sel_clk & 0x20) ||
- ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) {
+ if ((pll_lim->reg == NV_PRAMDAC_VPLL_COEFF && sel_clk & 0x20) ||
+ (pll_lim->reg == NV_RAMDAC_VPLL2 && sel_clk & 0x80)) {
if (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_CHIP_ID_INDEX) < 0xa3)
pll_lim->refclk = 200000;
else
@@ -4891,10 +4963,10 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
int i;
BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
- limit_match);
+ pll_lim->reg);
for (i = 0; i < entries; i++, entry += recordlen) {
- if (ROM32(entry[3]) == limit_match) {
+ if (ROM32(entry[3]) == pll_lim->reg) {
record = &bios->data[ROM16(entry[1])];
break;
}
@@ -4902,7 +4974,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
if (!record) {
NV_ERROR(dev, "Register 0x%08x not found in PLL "
- "limits table", limit_match);
+ "limits table", pll_lim->reg);
return -ENOENT;
}
@@ -4931,10 +5003,10 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
int i;
BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
- limit_match);
+ pll_lim->reg);
for (i = 0; i < entries; i++, entry += recordlen) {
- if (ROM32(entry[3]) == limit_match) {
+ if (ROM32(entry[3]) == pll_lim->reg) {
record = &bios->data[ROM16(entry[1])];
break;
}
@@ -4942,7 +5014,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
if (!record) {
NV_ERROR(dev, "Register 0x%08x not found in PLL "
- "limits table", limit_match);
+ "limits table", pll_lim->reg);
return -ENOENT;
}
@@ -5293,7 +5365,7 @@ parse_bit_M_tbl_entry(struct drm_device *dev, struct nvbios *bios,
if (bitentry->length < 0x5)
return 0;
- if (bitentry->id[1] < 2) {
+ if (bitentry->version < 2) {
bios->ram_restrict_group_count = bios->data[bitentry->offset + 2];
bios->ram_restrict_tbl_ptr = ROM16(bios->data[bitentry->offset + 3]);
} else {
@@ -5403,27 +5475,40 @@ struct bit_table {
#define BIT_TABLE(id, funcid) ((struct bit_table){ id, parse_bit_##funcid##_tbl_entry })
+int
+bit_table(struct drm_device *dev, u8 id, struct bit_entry *bit)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ u8 entries, *entry;
+
+ entries = bios->data[bios->offset + 10];
+ entry = &bios->data[bios->offset + 12];
+ while (entries--) {
+ if (entry[0] == id) {
+ bit->id = entry[0];
+ bit->version = entry[1];
+ bit->length = ROM16(entry[2]);
+ bit->offset = ROM16(entry[4]);
+ bit->data = ROMPTR(bios, entry[4]);
+ return 0;
+ }
+
+ entry += bios->data[bios->offset + 9];
+ }
+
+ return -ENOENT;
+}
+
static int
parse_bit_table(struct nvbios *bios, const uint16_t bitoffset,
struct bit_table *table)
{
struct drm_device *dev = bios->dev;
- uint8_t maxentries = bios->data[bitoffset + 4];
- int i, offset;
struct bit_entry bitentry;
- for (i = 0, offset = bitoffset + 6; i < maxentries; i++, offset += 6) {
- bitentry.id[0] = bios->data[offset];
-
- if (bitentry.id[0] != table->id)
- continue;
-
- bitentry.id[1] = bios->data[offset + 1];
- bitentry.length = ROM16(bios->data[offset + 2]);
- bitentry.offset = ROM16(bios->data[offset + 4]);
-
+ if (bit_table(dev, table->id, &bitentry) == 0)
return table->parse_fn(dev, bios, &bitentry);
- }
NV_INFO(dev, "BIT table '%c' not found\n", table->id);
return -ENOSYS;
@@ -5683,8 +5768,14 @@ static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
static struct dcb_gpio_entry *
new_gpio_entry(struct nvbios *bios)
{
+ struct drm_device *dev = bios->dev;
struct dcb_gpio_table *gpio = &bios->dcb.gpio;
+ if (gpio->entries >= DCB_MAX_NUM_GPIO_ENTRIES) {
+ NV_ERROR(dev, "exceeded maximum number of gpio entries!!\n");
+ return NULL;
+ }
+
return &gpio->entry[gpio->entries++];
}
@@ -5706,113 +5797,90 @@ nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag)
}
static void
-parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset)
-{
- struct dcb_gpio_entry *gpio;
- uint16_t ent = ROM16(bios->data[offset]);
- uint8_t line = ent & 0x1f,
- tag = ent >> 5 & 0x3f,
- flags = ent >> 11 & 0x1f;
-
- if (tag == 0x3f)
- return;
-
- gpio = new_gpio_entry(bios);
-
- gpio->tag = tag;
- gpio->line = line;
- gpio->invert = flags != 4;
- gpio->entry = ent;
-}
-
-static void
-parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset)
-{
- uint32_t entry = ROM32(bios->data[offset]);
- struct dcb_gpio_entry *gpio;
-
- if ((entry & 0x0000ff00) == 0x0000ff00)
- return;
-
- gpio = new_gpio_entry(bios);
- gpio->tag = (entry & 0x0000ff00) >> 8;
- gpio->line = (entry & 0x0000001f) >> 0;
- gpio->state_default = (entry & 0x01000000) >> 24;
- gpio->state[0] = (entry & 0x18000000) >> 27;
- gpio->state[1] = (entry & 0x60000000) >> 29;
- gpio->entry = entry;
-}
-
-static void
parse_dcb_gpio_table(struct nvbios *bios)
{
struct drm_device *dev = bios->dev;
- uint16_t gpio_table_ptr = bios->dcb.gpio_table_ptr;
- uint8_t *gpio_table = &bios->data[gpio_table_ptr];
- int header_len = gpio_table[1],
- entries = gpio_table[2],
- entry_len = gpio_table[3];
- void (*parse_entry)(struct nvbios *, uint16_t) = NULL;
+ struct dcb_gpio_entry *e;
+ u8 headerlen, entries, recordlen;
+ u8 *dcb, *gpio = NULL, *entry;
int i;
- if (bios->dcb.version >= 0x40) {
- if (gpio_table_ptr && entry_len != 4) {
- NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
- return;
- }
+ dcb = ROMPTR(bios, bios->data[0x36]);
+ if (dcb[0] >= 0x30) {
+ gpio = ROMPTR(bios, dcb[10]);
+ if (!gpio)
+ goto no_table;
- parse_entry = parse_dcb40_gpio_entry;
+ headerlen = gpio[1];
+ entries = gpio[2];
+ recordlen = gpio[3];
+ } else
+ if (dcb[0] >= 0x22 && dcb[-1] >= 0x13) {
+ gpio = ROMPTR(bios, dcb[-15]);
+ if (!gpio)
+ goto no_table;
+
+ headerlen = 3;
+ entries = gpio[2];
+ recordlen = gpio[1];
+ } else
+ if (dcb[0] >= 0x22) {
+ /* No GPIO table present, parse the TVDAC GPIO data. */
+ uint8_t *tvdac_gpio = &dcb[-5];
- } else if (bios->dcb.version >= 0x30) {
- if (gpio_table_ptr && entry_len != 2) {
- NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
- return;
+ if (tvdac_gpio[0] & 1) {
+ e = new_gpio_entry(bios);
+ e->tag = DCB_GPIO_TVDAC0;
+ e->line = tvdac_gpio[1] >> 4;
+ e->invert = tvdac_gpio[0] & 2;
}
- parse_entry = parse_dcb30_gpio_entry;
-
- } else if (bios->dcb.version >= 0x22) {
- /*
- * DCBs older than v3.0 don't really have a GPIO
- * table, instead they keep some GPIO info at fixed
- * locations.
- */
- uint16_t dcbptr = ROM16(bios->data[0x36]);
- uint8_t *tvdac_gpio = &bios->data[dcbptr - 5];
+ goto no_table;
+ } else {
+ NV_DEBUG(dev, "no/unknown gpio table on DCB 0x%02x\n", dcb[0]);
+ goto no_table;
+ }
- if (tvdac_gpio[0] & 1) {
- struct dcb_gpio_entry *gpio = new_gpio_entry(bios);
+ entry = gpio + headerlen;
+ for (i = 0; i < entries; i++, entry += recordlen) {
+ e = new_gpio_entry(bios);
+ if (!e)
+ break;
- gpio->tag = DCB_GPIO_TVDAC0;
- gpio->line = tvdac_gpio[1] >> 4;
- gpio->invert = tvdac_gpio[0] & 2;
- }
- } else {
- /*
- * No systematic way to store GPIO info on pre-v2.2
- * DCBs, try to match the PCI device IDs.
- */
+ if (gpio[0] < 0x40) {
+ e->entry = ROM16(entry[0]);
+ e->tag = (e->entry & 0x07e0) >> 5;
+ if (e->tag == 0x3f) {
+ bios->dcb.gpio.entries--;
+ continue;
+ }
- /* Apple iMac G4 NV18 */
- if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) {
- struct dcb_gpio_entry *gpio = new_gpio_entry(bios);
+ e->line = (e->entry & 0x001f);
+ e->invert = ((e->entry & 0xf800) >> 11) != 4;
+ } else {
+ e->entry = ROM32(entry[0]);
+ e->tag = (e->entry & 0x0000ff00) >> 8;
+ if (e->tag == 0xff) {
+ bios->dcb.gpio.entries--;
+ continue;
+ }
- gpio->tag = DCB_GPIO_TVDAC0;
- gpio->line = 4;
+ e->line = (e->entry & 0x0000001f) >> 0;
+ e->state_default = (e->entry & 0x01000000) >> 24;
+ e->state[0] = (e->entry & 0x18000000) >> 27;
+ e->state[1] = (e->entry & 0x60000000) >> 29;
}
-
}
- if (!gpio_table_ptr)
- return;
-
- if (entries > DCB_MAX_NUM_GPIO_ENTRIES) {
- NV_WARN(dev, "Too many entries in the DCB GPIO table.\n");
- entries = DCB_MAX_NUM_GPIO_ENTRIES;
+no_table:
+ /* Apple iMac G4 NV18 */
+ if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) {
+ e = new_gpio_entry(bios);
+ if (e) {
+ e->tag = DCB_GPIO_TVDAC0;
+ e->line = 4;
+ }
}
-
- for (i = 0; i < entries; i++)
- parse_entry(bios, gpio_table_ptr + header_len + entry_len * i);
}
struct dcb_connector_table_entry *
@@ -6680,6 +6748,8 @@ static int nouveau_parse_vbios_struct(struct drm_device *dev)
bit_signature, sizeof(bit_signature));
if (offset) {
NV_TRACE(dev, "BIT BIOS found\n");
+ bios->type = NVBIOS_BIT;
+ bios->offset = offset;
return parse_bit_structure(bios, offset + 6);
}
@@ -6687,6 +6757,8 @@ static int nouveau_parse_vbios_struct(struct drm_device *dev)
bmp_signature, sizeof(bmp_signature));
if (offset) {
NV_TRACE(dev, "BMP BIOS found\n");
+ bios->type = NVBIOS_BMP;
+ bios->offset = offset;
return parse_bmp_structure(dev, bios, offset);
}
@@ -6806,6 +6878,8 @@ nouveau_bios_init(struct drm_device *dev)
"running VBIOS init tables.\n");
bios->execute = true;
}
+ if (nouveau_force_post)
+ bios->execute = true;
ret = nouveau_run_vbios_init(dev);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index c1de2f3fcb0e..50a648e01c49 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -34,6 +34,20 @@
#define DCB_LOC_ON_CHIP 0
+#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
+#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
+#define ROMPTR(bios, x) (ROM16(x) ? &(bios)->data[ROM16(x)] : NULL)
+
+struct bit_entry {
+ uint8_t id;
+ uint8_t version;
+ uint16_t length;
+ uint16_t offset;
+ uint8_t *data;
+};
+
+int bit_table(struct drm_device *, u8 id, struct bit_entry *);
+
struct dcb_i2c_entry {
uint32_t entry;
uint8_t port_type;
@@ -170,16 +184,28 @@ enum LVDS_script {
LVDS_PANEL_OFF
};
-/* changing these requires matching changes to reg tables in nv_get_clock */
-#define MAX_PLL_TYPES 4
+/* these match types in pll limits table version 0x40,
+ * nouveau uses them on all chipsets internally where a
+ * specific pll needs to be referenced, but the exact
+ * register isn't known.
+ */
enum pll_types {
- NVPLL,
- MPLL,
- VPLL1,
- VPLL2
+ PLL_CORE = 0x01,
+ PLL_SHADER = 0x02,
+ PLL_UNK03 = 0x03,
+ PLL_MEMORY = 0x04,
+ PLL_UNK05 = 0x05,
+ PLL_UNK40 = 0x40,
+ PLL_UNK41 = 0x41,
+ PLL_UNK42 = 0x42,
+ PLL_VPLL0 = 0x80,
+ PLL_VPLL1 = 0x81,
+ PLL_MAX = 0xff
};
struct pll_lims {
+ u32 reg;
+
struct {
int minfreq;
int maxfreq;
@@ -212,6 +238,11 @@ struct pll_lims {
struct nvbios {
struct drm_device *dev;
+ enum {
+ NVBIOS_BMP,
+ NVBIOS_BIT
+ } type;
+ uint16_t offset;
uint8_t chip_version;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index f6f44779d82f..80353e2b8409 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -36,21 +36,6 @@
#include <linux/log2.h>
#include <linux/slab.h>
-int
-nouveau_bo_sync_gpu(struct nouveau_bo *nvbo, struct nouveau_channel *chan)
-{
- struct nouveau_fence *prev_fence = nvbo->bo.sync_obj;
- int ret;
-
- if (!prev_fence || nouveau_fence_channel(prev_fence) == chan)
- return 0;
-
- spin_lock(&nvbo->bo.lock);
- ret = ttm_bo_wait(&nvbo->bo, false, false, false);
- spin_unlock(&nvbo->bo.lock);
- return ret;
-}
-
static void
nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
{
@@ -58,8 +43,6 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
struct drm_device *dev = dev_priv->dev;
struct nouveau_bo *nvbo = nouveau_bo(bo);
- ttm_bo_kunmap(&nvbo->kmap);
-
if (unlikely(nvbo->gem))
DRM_ERROR("bo %p still attached to GEM object\n", bo);
@@ -164,8 +147,6 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
nouveau_bo_fixup_align(dev, tile_mode, tile_flags, &align, &size);
align >>= PAGE_SHIFT;
- nvbo->placement.fpfn = 0;
- nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0;
nouveau_bo_placement_set(nvbo, flags, 0);
nvbo->channel = chan;
@@ -305,7 +286,8 @@ nouveau_bo_map(struct nouveau_bo *nvbo)
void
nouveau_bo_unmap(struct nouveau_bo *nvbo)
{
- ttm_bo_kunmap(&nvbo->kmap);
+ if (nvbo)
+ ttm_bo_kunmap(&nvbo->kmap);
}
u16
@@ -399,14 +381,19 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
+ man->func = &ttm_bo_manager_func;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
- man->gpu_offset = dev_priv->vm_vram_base;
+ if (dev_priv->card_type == NV_50)
+ man->gpu_offset = 0x40000000;
+ else
+ man->gpu_offset = 0;
break;
case TTM_PL_TT:
+ man->func = &ttm_bo_manager_func;
switch (dev_priv->gart_info.type) {
case NOUVEAU_GART_AGP:
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
@@ -469,19 +456,26 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
if (ret)
return ret;
- ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL,
- evict || (nvbo->channel &&
- nvbo->channel != chan),
+ if (nvbo->channel) {
+ ret = nouveau_fence_sync(fence, nvbo->channel);
+ if (ret)
+ goto out;
+ }
+
+ ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL, evict,
no_wait_reserve, no_wait_gpu, new_mem);
+out:
nouveau_fence_unref((void *)&fence);
return ret;
}
static inline uint32_t
-nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
- struct ttm_mem_reg *mem)
+nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo,
+ struct nouveau_channel *chan, struct ttm_mem_reg *mem)
{
- if (chan == nouveau_bdev(nvbo->bo.bdev)->channel) {
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
+
+ if (nvbo->no_vm) {
if (mem->mem_type == TTM_PL_TT)
return NvDmaGART;
return NvDmaVRAM;
@@ -493,86 +487,181 @@ nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
}
static int
-nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
- bool no_wait_reserve, bool no_wait_gpu,
- struct ttm_mem_reg *new_mem)
+nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,
+ struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
{
- struct nouveau_bo *nvbo = nouveau_bo(bo);
struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
- struct ttm_mem_reg *old_mem = &bo->mem;
- struct nouveau_channel *chan;
- uint64_t src_offset, dst_offset;
- uint32_t page_count;
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
+ u64 length = (new_mem->num_pages << PAGE_SHIFT);
+ u64 src_offset, dst_offset;
int ret;
- chan = nvbo->channel;
- if (!chan || nvbo->tile_flags || nvbo->no_vm)
- chan = dev_priv->channel;
-
- src_offset = old_mem->mm_node->start << PAGE_SHIFT;
- dst_offset = new_mem->mm_node->start << PAGE_SHIFT;
- if (chan != dev_priv->channel) {
- if (old_mem->mem_type == TTM_PL_TT)
- src_offset += dev_priv->vm_gart_base;
- else
+ src_offset = old_mem->start << PAGE_SHIFT;
+ dst_offset = new_mem->start << PAGE_SHIFT;
+ if (!nvbo->no_vm) {
+ if (old_mem->mem_type == TTM_PL_VRAM)
src_offset += dev_priv->vm_vram_base;
-
- if (new_mem->mem_type == TTM_PL_TT)
- dst_offset += dev_priv->vm_gart_base;
else
+ src_offset += dev_priv->vm_gart_base;
+
+ if (new_mem->mem_type == TTM_PL_VRAM)
dst_offset += dev_priv->vm_vram_base;
+ else
+ dst_offset += dev_priv->vm_gart_base;
}
ret = RING_SPACE(chan, 3);
if (ret)
return ret;
- BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2);
- OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, old_mem));
- OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, new_mem));
- if (dev_priv->card_type >= NV_50) {
- ret = RING_SPACE(chan, 4);
+ BEGIN_RING(chan, NvSubM2MF, 0x0184, 2);
+ OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_mem));
+ OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem));
+
+ while (length) {
+ u32 amount, stride, height;
+
+ amount = min(length, (u64)(4 * 1024 * 1024));
+ stride = 16 * 4;
+ height = amount / stride;
+
+ if (new_mem->mem_type == TTM_PL_VRAM && nvbo->tile_flags) {
+ ret = RING_SPACE(chan, 8);
+ if (ret)
+ return ret;
+
+ BEGIN_RING(chan, NvSubM2MF, 0x0200, 7);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, stride);
+ OUT_RING (chan, height);
+ OUT_RING (chan, 1);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 0);
+ } else {
+ ret = RING_SPACE(chan, 2);
+ if (ret)
+ return ret;
+
+ BEGIN_RING(chan, NvSubM2MF, 0x0200, 1);
+ OUT_RING (chan, 1);
+ }
+ if (old_mem->mem_type == TTM_PL_VRAM && nvbo->tile_flags) {
+ ret = RING_SPACE(chan, 8);
+ if (ret)
+ return ret;
+
+ BEGIN_RING(chan, NvSubM2MF, 0x021c, 7);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, stride);
+ OUT_RING (chan, height);
+ OUT_RING (chan, 1);
+ OUT_RING (chan, 0);
+ OUT_RING (chan, 0);
+ } else {
+ ret = RING_SPACE(chan, 2);
+ if (ret)
+ return ret;
+
+ BEGIN_RING(chan, NvSubM2MF, 0x021c, 1);
+ OUT_RING (chan, 1);
+ }
+
+ ret = RING_SPACE(chan, 14);
if (ret)
return ret;
- BEGIN_RING(chan, NvSubM2MF, 0x0200, 1);
- OUT_RING(chan, 1);
- BEGIN_RING(chan, NvSubM2MF, 0x021c, 1);
- OUT_RING(chan, 1);
+
+ BEGIN_RING(chan, NvSubM2MF, 0x0238, 2);
+ OUT_RING (chan, upper_32_bits(src_offset));
+ OUT_RING (chan, upper_32_bits(dst_offset));
+ BEGIN_RING(chan, NvSubM2MF, 0x030c, 8);
+ OUT_RING (chan, lower_32_bits(src_offset));
+ OUT_RING (chan, lower_32_bits(dst_offset));
+ OUT_RING (chan, stride);
+ OUT_RING (chan, stride);
+ OUT_RING (chan, stride);
+ OUT_RING (chan, height);
+ OUT_RING (chan, 0x00000101);
+ OUT_RING (chan, 0x00000000);
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1);
+ OUT_RING (chan, 0);
+
+ length -= amount;
+ src_offset += amount;
+ dst_offset += amount;
}
+ return 0;
+}
+
+static int
+nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,
+ struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
+{
+ u32 src_offset = old_mem->start << PAGE_SHIFT;
+ u32 dst_offset = new_mem->start << PAGE_SHIFT;
+ u32 page_count = new_mem->num_pages;
+ int ret;
+
+ ret = RING_SPACE(chan, 3);
+ if (ret)
+ return ret;
+
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2);
+ OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_mem));
+ OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem));
+
page_count = new_mem->num_pages;
while (page_count) {
int line_count = (page_count > 2047) ? 2047 : page_count;
- if (dev_priv->card_type >= NV_50) {
- ret = RING_SPACE(chan, 3);
- if (ret)
- return ret;
- BEGIN_RING(chan, NvSubM2MF, 0x0238, 2);
- OUT_RING(chan, upper_32_bits(src_offset));
- OUT_RING(chan, upper_32_bits(dst_offset));
- }
ret = RING_SPACE(chan, 11);
if (ret)
return ret;
+
BEGIN_RING(chan, NvSubM2MF,
NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
- OUT_RING(chan, lower_32_bits(src_offset));
- OUT_RING(chan, lower_32_bits(dst_offset));
- OUT_RING(chan, PAGE_SIZE); /* src_pitch */
- OUT_RING(chan, PAGE_SIZE); /* dst_pitch */
- OUT_RING(chan, PAGE_SIZE); /* line_length */
- OUT_RING(chan, line_count);
- OUT_RING(chan, (1<<8)|(1<<0));
- OUT_RING(chan, 0);
+ OUT_RING (chan, src_offset);
+ OUT_RING (chan, dst_offset);
+ OUT_RING (chan, PAGE_SIZE); /* src_pitch */
+ OUT_RING (chan, PAGE_SIZE); /* dst_pitch */
+ OUT_RING (chan, PAGE_SIZE); /* line_length */
+ OUT_RING (chan, line_count);
+ OUT_RING (chan, 0x00000101);
+ OUT_RING (chan, 0x00000000);
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1);
- OUT_RING(chan, 0);
+ OUT_RING (chan, 0);
page_count -= line_count;
src_offset += (PAGE_SIZE * line_count);
dst_offset += (PAGE_SIZE * line_count);
}
+ return 0;
+}
+
+static int
+nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
+ bool no_wait_reserve, bool no_wait_gpu,
+ struct ttm_mem_reg *new_mem)
+{
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
+ struct nouveau_channel *chan;
+ int ret;
+
+ chan = nvbo->channel;
+ if (!chan || nvbo->no_vm)
+ chan = dev_priv->channel;
+
+ if (dev_priv->card_type < NV_50)
+ ret = nv04_bo_move_m2mf(chan, bo, &bo->mem, new_mem);
+ else
+ ret = nv50_bo_move_m2mf(chan, bo, &bo->mem, new_mem);
+ if (ret)
+ return ret;
+
return nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait_reserve, no_wait_gpu, new_mem);
}
@@ -606,12 +695,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
ret = ttm_bo_move_ttm(bo, evict, no_wait_reserve, no_wait_gpu, new_mem);
out:
- if (tmp_mem.mm_node) {
- spin_lock(&bo->bdev->glob->lru_lock);
- drm_mm_put_block(tmp_mem.mm_node);
- spin_unlock(&bo->bdev->glob->lru_lock);
- }
-
+ ttm_bo_mem_put(bo, &tmp_mem);
return ret;
}
@@ -644,12 +728,7 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
goto out;
out:
- if (tmp_mem.mm_node) {
- spin_lock(&bo->bdev->glob->lru_lock);
- drm_mm_put_block(tmp_mem.mm_node);
- spin_unlock(&bo->bdev->glob->lru_lock);
- }
-
+ ttm_bo_mem_put(bo, &tmp_mem);
return ret;
}
@@ -669,7 +748,7 @@ nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem,
return 0;
}
- offset = new_mem->mm_node->start << PAGE_SHIFT;
+ offset = new_mem->start << PAGE_SHIFT;
if (dev_priv->card_type == NV_50) {
ret = nv50_mem_vm_bind_linear(dev,
@@ -719,12 +798,6 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
if (ret)
return ret;
- /* Software copy if the card isn't up and running yet. */
- if (!dev_priv->channel) {
- ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem);
- goto out;
- }
-
/* Fake bo copy. */
if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
BUG_ON(bo->mem.mm_node != NULL);
@@ -733,6 +806,12 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
goto out;
}
+ /* Software copy if the card isn't up and running yet. */
+ if (!dev_priv->channel) {
+ ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem);
+ goto out;
+ }
+
/* Hardware assisted copy. */
if (new_mem->mem_type == TTM_PL_SYSTEM)
ret = nouveau_bo_move_flipd(bo, evict, intr, no_wait_reserve, no_wait_gpu, new_mem);
@@ -783,14 +862,14 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
case TTM_PL_TT:
#if __OS_HAS_AGP
if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) {
- mem->bus.offset = mem->mm_node->start << PAGE_SHIFT;
+ mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = dev_priv->gart_info.aper_base;
mem->bus.is_iomem = true;
}
#endif
break;
case TTM_PL_VRAM:
- mem->bus.offset = mem->mm_node->start << PAGE_SHIFT;
+ mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = pci_resource_start(dev->pdev, 1);
mem->bus.is_iomem = true;
break;
@@ -808,7 +887,26 @@ nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
static int
nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
{
- return 0;
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
+
+ /* as long as the bo isn't in vram, and isn't tiled, we've got
+ * nothing to do here.
+ */
+ if (bo->mem.mem_type != TTM_PL_VRAM) {
+ if (dev_priv->card_type < NV_50 || !nvbo->tile_flags)
+ return 0;
+ }
+
+ /* make sure bo is in mappable vram */
+ if (bo->mem.start + bo->mem.num_pages < dev_priv->fb_mappable_pages)
+ return 0;
+
+
+ nvbo->placement.fpfn = 0;
+ nvbo->placement.lpfn = dev_priv->fb_mappable_pages;
+ nouveau_bo_placement_set(nvbo, TTM_PL_VRAM, 0);
+ return ttm_bo_validate(bo, &nvbo->placement, false, true, false);
}
struct ttm_bo_driver nouveau_bo_driver = {
diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c
index ca85da784846..dad96cce5e39 100644
--- a/drivers/gpu/drm/nouveau/nouveau_calc.c
+++ b/drivers/gpu/drm/nouveau/nouveau_calc.c
@@ -198,8 +198,8 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv_fifo_info fifo_data;
struct nv_sim_state sim_data;
- int MClk = nouveau_hw_get_clock(dev, MPLL);
- int NVClk = nouveau_hw_get_clock(dev, NVPLL);
+ int MClk = nouveau_hw_get_clock(dev, PLL_MEMORY);
+ int NVClk = nouveau_hw_get_clock(dev, PLL_CORE);
uint32_t cfg1 = nvReadFB(dev, NV04_PFB_CFG1);
sim_data.pclk_khz = VClk;
@@ -234,7 +234,7 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
}
static void
-nv30_update_arb(int *burst, int *lwm)
+nv20_update_arb(int *burst, int *lwm)
{
unsigned int fifo_size, burst_size, graphics_lwm;
@@ -251,14 +251,14 @@ nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- if (dev_priv->card_type < NV_30)
+ if (dev_priv->card_type < NV_20)
nv04_update_arb(dev, vclk, bpp, burst, lwm);
else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
(dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
*burst = 128;
*lwm = 0x0480;
} else
- nv30_update_arb(burst, lwm);
+ nv20_update_arb(burst, lwm);
}
static int
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index 0480f064f2c1..373950e34814 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -48,14 +48,14 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
dev_priv->gart_info.aper_size,
NV_DMA_ACCESS_RO, &pushbuf,
NULL);
- chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT;
+ chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
} else
if (dev_priv->card_type != NV_04) {
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
dev_priv->fb_available_size,
NV_DMA_ACCESS_RO,
NV_DMA_TARGET_VIDMEM, &pushbuf);
- chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT;
+ chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
} else {
/* NV04 cmdbuf hack, from original ddx.. not sure of it's
* exact reason for existing :) PCI access to cmdbuf in
@@ -67,17 +67,11 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
dev_priv->fb_available_size,
NV_DMA_ACCESS_RO,
NV_DMA_TARGET_PCI, &pushbuf);
- chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT;
- }
-
- ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf);
- if (ret) {
- NV_ERROR(dev, "Error referencing pushbuf ctxdma: %d\n", ret);
- if (pushbuf != dev_priv->gart_info.sg_ctxdma)
- nouveau_gpuobj_del(dev, &pushbuf);
- return ret;
+ chan->pushbuf_base = pb->bo.mem.start << PAGE_SHIFT;
}
+ nouveau_gpuobj_ref(pushbuf, &chan->pushbuf);
+ nouveau_gpuobj_ref(NULL, &pushbuf);
return 0;
}
@@ -229,7 +223,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
ret = nouveau_dma_init(chan);
if (!ret)
- ret = nouveau_fence_init(chan);
+ ret = nouveau_fence_channel_init(chan);
if (ret) {
nouveau_channel_free(chan);
return ret;
@@ -276,7 +270,7 @@ nouveau_channel_free(struct nouveau_channel *chan)
* above attempts at idling were OK, but if we failed this'll tell TTM
* we're done with the buffers.
*/
- nouveau_fence_fini(chan);
+ nouveau_fence_channel_fini(chan);
/* This will prevent pfifo from switching channels. */
pfifo->reassign(dev, false);
@@ -308,8 +302,9 @@ nouveau_channel_free(struct nouveau_channel *chan)
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
/* Release the channel's resources */
- nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
+ nouveau_gpuobj_ref(NULL, &chan->pushbuf);
if (chan->pushbuf_bo) {
+ nouveau_bo_unmap(chan->pushbuf_bo);
nouveau_bo_unpin(chan->pushbuf_bo);
nouveau_bo_ref(NULL, &chan->pushbuf_bo);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index fc737037f751..0871495096fa 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -76,6 +76,22 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
return NULL;
}
+/*TODO: This could use improvement, and learn to handle the fixed
+ * BIOS tables etc. It's fine currently, for its only user.
+ */
+int
+nouveau_connector_bpp(struct drm_connector *connector)
+{
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
+
+ if (nv_connector->edid && nv_connector->edid->revision >= 4) {
+ u8 bpc = ((nv_connector->edid->input & 0x70) >> 3) + 4;
+ if (bpc > 4)
+ return bpc;
+ }
+
+ return 18;
+}
static void
nouveau_connector_destroy(struct drm_connector *drm_connector)
@@ -130,6 +146,36 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
return NULL;
}
+static struct nouveau_encoder *
+nouveau_connector_of_detect(struct drm_connector *connector)
+{
+#ifdef __powerpc__
+ struct drm_device *dev = connector->dev;
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_encoder *nv_encoder;
+ struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev);
+
+ if (!dn ||
+ !((nv_encoder = find_encoder_by_type(connector, OUTPUT_TMDS)) ||
+ (nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG))))
+ return NULL;
+
+ for_each_child_of_node(dn, cn) {
+ const char *name = of_get_property(cn, "name", NULL);
+ const void *edid = of_get_property(cn, "EDID", NULL);
+ int idx = name ? name[strlen(name) - 1] - 'A' : 0;
+
+ if (nv_encoder->dcb->i2c_index == idx && edid) {
+ nv_connector->edid =
+ kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
+ of_node_put(cn);
+ return nv_encoder;
+ }
+ }
+#endif
+ return NULL;
+}
+
static void
nouveau_connector_set_encoder(struct drm_connector *connector,
struct nouveau_encoder *nv_encoder)
@@ -225,6 +271,12 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
return connector_status_connected;
}
+ nv_encoder = nouveau_connector_of_detect(connector);
+ if (nv_encoder) {
+ nouveau_connector_set_encoder(connector, nv_encoder);
+ return connector_status_connected;
+ }
+
detect_analog:
nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
if (!nv_encoder && !nouveau_tv_disable)
@@ -630,7 +682,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
else
max_clock = nv_encoder->dp.link_nr * 162000;
- clock *= 3;
+ clock = clock * nouveau_connector_bpp(connector) / 8;
break;
default:
BUG_ON(1);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 0d2e668ccfe5..c21ed6b16f88 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -55,4 +55,7 @@ nouveau_connector_create(struct drm_device *, int index);
void
nouveau_connector_set_polling(struct drm_connector *);
+int
+nouveau_connector_bpp(struct drm_connector *);
+
#endif /* __NOUVEAU_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
index 7933de4aff2e..8e1592368cce 100644
--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
@@ -157,7 +157,23 @@ nouveau_debugfs_vbios_image(struct seq_file *m, void *data)
return 0;
}
+static int
+nouveau_debugfs_evict_vram(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_nouveau_private *dev_priv = node->minor->dev->dev_private;
+ int ret;
+
+ ret = ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
+ if (ret)
+ seq_printf(m, "failed: %d", ret);
+ else
+ seq_printf(m, "succeeded\n");
+ return 0;
+}
+
static struct drm_info_list nouveau_debugfs_list[] = {
+ { "evict_vram", nouveau_debugfs_evict_vram, 0, NULL },
{ "chipset", nouveau_debugfs_chipset_info, 0, NULL },
{ "memory", nouveau_debugfs_memory_info, 0, NULL },
{ "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL },
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index 2e3c6caa97ee..82581e600dcd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -28,6 +28,7 @@
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
+#include "nouveau_ramht.h"
void
nouveau_dma_pre_init(struct nouveau_channel *chan)
@@ -58,26 +59,17 @@ nouveau_dma_init(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *m2mf = NULL;
- struct nouveau_gpuobj *nvsw = NULL;
+ struct nouveau_gpuobj *obj = NULL;
int ret, i;
/* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ?
- 0x0039 : 0x5039, &m2mf);
+ 0x0039 : 0x5039, &obj);
if (ret)
return ret;
- ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL);
- if (ret)
- return ret;
-
- /* Create an NV_SW object for various sync purposes */
- ret = nouveau_gpuobj_sw_new(chan, NV_SW, &nvsw);
- if (ret)
- return ret;
-
- ret = nouveau_gpuobj_ref_add(dev, chan, NvSw, nvsw, NULL);
+ ret = nouveau_ramht_insert(chan, NvM2MF, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
if (ret)
return ret;
@@ -91,11 +83,6 @@ nouveau_dma_init(struct nouveau_channel *chan)
if (ret)
return ret;
- /* Map M2MF notifier object - fbcon. */
- ret = nouveau_bo_map(chan->notifier_bo);
- if (ret)
- return ret;
-
/* Insert NOPS for NOUVEAU_DMA_SKIPS */
ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
if (ret)
@@ -113,13 +100,6 @@ nouveau_dma_init(struct nouveau_channel *chan)
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
OUT_RING(chan, NvNotify0);
- /* Initialise NV_SW */
- ret = RING_SPACE(chan, 2);
- if (ret)
- return ret;
- BEGIN_RING(chan, NvSubSw, 0, 1);
- OUT_RING(chan, NvSw);
-
/* Sit back and pray the channel works.. */
FIRE_RING(chan);
@@ -217,7 +197,7 @@ nv50_dma_push_wait(struct nouveau_channel *chan, int count)
chan->dma.ib_free = get - chan->dma.ib_put;
if (chan->dma.ib_free <= 0)
- chan->dma.ib_free += chan->dma.ib_max + 1;
+ chan->dma.ib_free += chan->dma.ib_max;
}
return 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h
index 8b05c15866d5..d578c21d3c8d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.h
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.h
@@ -72,6 +72,7 @@ enum {
NvGdiRect = 0x8000000c,
NvImageBlit = 0x8000000d,
NvSw = 0x8000000e,
+ NvSema = 0x8000000f,
/* G80+ display objects */
NvEvoVRAM = 0x01000000,
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 8a1b188b4cd1..4562f309ae3d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -317,7 +317,8 @@ train:
return false;
config[0] = nv_encoder->dp.link_nr;
- if (nv_encoder->dp.dpcd_version >= 0x11)
+ if (nv_encoder->dp.dpcd_version >= 0x11 &&
+ nv_encoder->dp.enhanced_frame)
config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
ret = nouveau_dp_lane_count_set(encoder, config[0]);
@@ -468,10 +469,12 @@ nouveau_dp_detect(struct drm_encoder *encoder)
!nv_encoder->dcb->dpconf.link_bw)
nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
- nv_encoder->dp.link_nr = dpcd[2] & 0xf;
+ nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr)
nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
+ nv_encoder->dp.enhanced_frame = (dpcd[2] & DP_ENHANCED_FRAME_CAP);
+
return true;
}
@@ -524,7 +527,8 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000);
nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl);
nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000);
- if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) {
+ if (!nv_wait(dev, NV50_AUXCH_CTRL(index),
+ 0x00010000, 0x00000000)) {
NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
nv_rd32(dev, NV50_AUXCH_CTRL(index)));
ret = -EBUSY;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index eb15345162a0..90875494a65a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -31,13 +31,14 @@
#include "nouveau_hw.h"
#include "nouveau_fb.h"
#include "nouveau_fbcon.h"
+#include "nouveau_pm.h"
#include "nv50_display.h"
#include "drm_pciids.h"
-MODULE_PARM_DESC(noagp, "Disable AGP");
-int nouveau_noagp;
-module_param_named(noagp, nouveau_noagp, int, 0400);
+MODULE_PARM_DESC(agpmode, "AGP mode (0 to disable AGP)");
+int nouveau_agpmode = -1;
+module_param_named(agpmode, nouveau_agpmode, int, 0400);
MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
static int nouveau_modeset = -1; /* kms */
@@ -79,6 +80,10 @@ MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
int nouveau_nofbaccel = 0;
module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
+MODULE_PARM_DESC(force_post, "Force POST");
+int nouveau_force_post = 0;
+module_param_named(force_post, nouveau_force_post, int, 0400);
+
MODULE_PARM_DESC(override_conntype, "Ignore DCB connector type");
int nouveau_override_conntype = 0;
module_param_named(override_conntype, nouveau_override_conntype, int, 0400);
@@ -102,6 +107,14 @@ MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n"
int nouveau_reg_debug;
module_param_named(reg_debug, nouveau_reg_debug, int, 0600);
+MODULE_PARM_DESC(perflvl, "Performance level (default: boot)\n");
+char *nouveau_perflvl;
+module_param_named(perflvl, nouveau_perflvl, charp, 0400);
+
+MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)\n");
+int nouveau_perflvl_wr;
+module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400);
+
int nouveau_fbpercrtc;
#if 0
module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
@@ -271,6 +284,8 @@ nouveau_pci_resume(struct pci_dev *pdev)
if (ret)
return ret;
+ nouveau_pm_resume(dev);
+
if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) {
ret = nouveau_mem_init_agp(dev);
if (ret) {
@@ -379,8 +394,6 @@ static struct drm_driver driver = {
.irq_uninstall = nouveau_irq_uninstall,
.irq_handler = nouveau_irq_handler,
.reclaim_buffers = drm_core_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = nouveau_ioctls,
.fops = {
.owner = THIS_MODULE,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index b1be617373b6..3a07e580d27a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -133,22 +133,24 @@ enum nouveau_flags {
#define NVOBJ_ENGINE_DISPLAY 2
#define NVOBJ_ENGINE_INT 0xdeadbeef
-#define NVOBJ_FLAG_ALLOW_NO_REFS (1 << 0)
#define NVOBJ_FLAG_ZERO_ALLOC (1 << 1)
#define NVOBJ_FLAG_ZERO_FREE (1 << 2)
-#define NVOBJ_FLAG_FAKE (1 << 3)
struct nouveau_gpuobj {
+ struct drm_device *dev;
+ struct kref refcount;
struct list_head list;
- struct nouveau_channel *im_channel;
struct drm_mm_node *im_pramin;
struct nouveau_bo *im_backing;
- uint32_t im_backing_start;
uint32_t *im_backing_suspend;
int im_bound;
uint32_t flags;
- int refcount;
+
+ u32 size;
+ u32 pinst;
+ u32 cinst;
+ u64 vinst;
uint32_t engine;
uint32_t class;
@@ -157,16 +159,6 @@ struct nouveau_gpuobj {
void *priv;
};
-struct nouveau_gpuobj_ref {
- struct list_head list;
-
- struct nouveau_gpuobj *gpuobj;
- uint32_t instance;
-
- struct nouveau_channel *channel;
- int handle;
-};
-
struct nouveau_channel {
struct drm_device *dev;
int id;
@@ -192,33 +184,32 @@ struct nouveau_channel {
} fence;
/* DMA push buffer */
- struct nouveau_gpuobj_ref *pushbuf;
- struct nouveau_bo *pushbuf_bo;
- uint32_t pushbuf_base;
+ struct nouveau_gpuobj *pushbuf;
+ struct nouveau_bo *pushbuf_bo;
+ uint32_t pushbuf_base;
/* Notifier memory */
struct nouveau_bo *notifier_bo;
struct drm_mm notifier_heap;
/* PFIFO context */
- struct nouveau_gpuobj_ref *ramfc;
- struct nouveau_gpuobj_ref *cache;
+ struct nouveau_gpuobj *ramfc;
+ struct nouveau_gpuobj *cache;
/* PGRAPH context */
/* XXX may be merge 2 pointers as private data ??? */
- struct nouveau_gpuobj_ref *ramin_grctx;
+ struct nouveau_gpuobj *ramin_grctx;
void *pgraph_ctx;
/* NV50 VM */
- struct nouveau_gpuobj *vm_pd;
- struct nouveau_gpuobj_ref *vm_gart_pt;
- struct nouveau_gpuobj_ref *vm_vram_pt[NV50_VM_VRAM_NR];
+ struct nouveau_gpuobj *vm_pd;
+ struct nouveau_gpuobj *vm_gart_pt;
+ struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
/* Objects */
- struct nouveau_gpuobj_ref *ramin; /* Private instmem */
- struct drm_mm ramin_heap; /* Private PRAMIN heap */
- struct nouveau_gpuobj_ref *ramht; /* Hash table */
- struct list_head ramht_refs; /* Objects referenced by RAMHT */
+ struct nouveau_gpuobj *ramin; /* Private instmem */
+ struct drm_mm ramin_heap; /* Private PRAMIN heap */
+ struct nouveau_ramht *ramht; /* Hash table */
/* GPU object info for stuff used in-kernel (mm_enabled) */
uint32_t m2mf_ntfy;
@@ -296,7 +287,7 @@ struct nouveau_fb_engine {
struct nouveau_fifo_engine {
int channels;
- struct nouveau_gpuobj_ref *playlist[2];
+ struct nouveau_gpuobj *playlist[2];
int cur_playlist;
int (*init)(struct drm_device *);
@@ -305,7 +296,6 @@ struct nouveau_fifo_engine {
void (*disable)(struct drm_device *);
void (*enable)(struct drm_device *);
bool (*reassign)(struct drm_device *, bool enable);
- bool (*cache_flush)(struct drm_device *dev);
bool (*cache_pull)(struct drm_device *dev, bool enable);
int (*channel_id)(struct drm_device *);
@@ -334,7 +324,7 @@ struct nouveau_pgraph_engine {
int grctx_size;
/* NV2x/NV3x context table (0x400780) */
- struct nouveau_gpuobj_ref *ctx_table;
+ struct nouveau_gpuobj *ctx_table;
int (*init)(struct drm_device *);
void (*takedown)(struct drm_device *);
@@ -369,6 +359,91 @@ struct nouveau_gpio_engine {
void (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on);
};
+struct nouveau_pm_voltage_level {
+ u8 voltage;
+ u8 vid;
+};
+
+struct nouveau_pm_voltage {
+ bool supported;
+ u8 vid_mask;
+
+ struct nouveau_pm_voltage_level *level;
+ int nr_level;
+};
+
+#define NOUVEAU_PM_MAX_LEVEL 8
+struct nouveau_pm_level {
+ struct device_attribute dev_attr;
+ char name[32];
+ int id;
+
+ u32 core;
+ u32 memory;
+ u32 shader;
+ u32 unk05;
+
+ u8 voltage;
+ u8 fanspeed;
+
+ u16 memscript;
+};
+
+struct nouveau_pm_temp_sensor_constants {
+ u16 offset_constant;
+ s16 offset_mult;
+ u16 offset_div;
+ u16 slope_mult;
+ u16 slope_div;
+};
+
+struct nouveau_pm_threshold_temp {
+ s16 critical;
+ s16 down_clock;
+ s16 fan_boost;
+};
+
+struct nouveau_pm_memtiming {
+ u32 reg_100220;
+ u32 reg_100224;
+ u32 reg_100228;
+ u32 reg_10022c;
+ u32 reg_100230;
+ u32 reg_100234;
+ u32 reg_100238;
+ u32 reg_10023c;
+};
+
+struct nouveau_pm_memtimings {
+ bool supported;
+ struct nouveau_pm_memtiming *timing;
+ int nr_timing;
+};
+
+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_level boot;
+ struct nouveau_pm_level *cur;
+
+ struct device *hwmon;
+
+ int (*clock_get)(struct drm_device *, u32 id);
+ void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *,
+ u32 id, int khz);
+ void (*clock_set)(struct drm_device *, void *);
+ int (*voltage_get)(struct drm_device *);
+ int (*voltage_set)(struct drm_device *, int voltage);
+ int (*fanspeed_get)(struct drm_device *);
+ int (*fanspeed_set)(struct drm_device *, int fanspeed);
+ int (*temp_get)(struct drm_device *);
+};
+
struct nouveau_engine {
struct nouveau_instmem_engine instmem;
struct nouveau_mc_engine mc;
@@ -378,6 +453,7 @@ struct nouveau_engine {
struct nouveau_fifo_engine fifo;
struct nouveau_display_engine display;
struct nouveau_gpio_engine gpio;
+ struct nouveau_pm_engine pm;
};
struct nouveau_pll_vals {
@@ -522,8 +598,14 @@ struct drm_nouveau_private {
int flags;
void __iomem *mmio;
+
+ spinlock_t ramin_lock;
void __iomem *ramin;
- uint32_t ramin_size;
+ u32 ramin_size;
+ u32 ramin_base;
+ bool ramin_available;
+ struct drm_mm ramin_heap;
+ struct list_head gpuobj_list;
struct nouveau_bo *vga_ram;
@@ -540,6 +622,12 @@ struct drm_nouveau_private {
atomic_t validate_sequence;
} ttm;
+ struct {
+ spinlock_t lock;
+ struct drm_mm heap;
+ struct nouveau_bo *bo;
+ } fence;
+
int fifo_alloc_count;
struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
@@ -550,15 +638,11 @@ struct drm_nouveau_private {
spinlock_t context_switch_lock;
/* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
- struct nouveau_gpuobj *ramht;
+ struct nouveau_ramht *ramht;
+ struct nouveau_gpuobj *ramfc;
+ struct nouveau_gpuobj *ramro;
+
uint32_t ramin_rsvd_vram;
- uint32_t ramht_offset;
- uint32_t ramht_size;
- uint32_t ramht_bits;
- uint32_t ramfc_offset;
- uint32_t ramfc_size;
- uint32_t ramro_offset;
- uint32_t ramro_size;
struct {
enum {
@@ -576,14 +660,12 @@ struct drm_nouveau_private {
} gart_info;
/* nv10-nv40 tiling regions */
- struct {
- struct nouveau_tile_reg reg[NOUVEAU_MAX_TILE_NR];
- spinlock_t lock;
- } tile;
+ struct nouveau_tile_reg tile[NOUVEAU_MAX_TILE_NR];
/* VRAM/fb configuration */
uint64_t vram_size;
uint64_t vram_sys_base;
+ u32 vram_rblock_size;
uint64_t fb_phys;
uint64_t fb_available_size;
@@ -600,10 +682,6 @@ struct drm_nouveau_private {
struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
int vm_vram_pt_nr;
- struct drm_mm ramin_heap;
-
- struct list_head gpuobj_list;
-
struct nvbios vbios;
struct nv04_mode_state mode_reg;
@@ -634,6 +712,12 @@ struct drm_nouveau_private {
};
static inline struct drm_nouveau_private *
+nouveau_private(struct drm_device *dev)
+{
+ return dev->dev_private;
+}
+
+static inline struct drm_nouveau_private *
nouveau_bdev(struct ttm_bo_device *bd)
{
return container_of(bd, struct drm_nouveau_private, ttm.bdev);
@@ -669,7 +753,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
} while (0)
/* nouveau_drv.c */
-extern int nouveau_noagp;
+extern int nouveau_agpmode;
extern int nouveau_duallink;
extern int nouveau_uscript_lvds;
extern int nouveau_uscript_tmds;
@@ -683,7 +767,10 @@ extern char *nouveau_vbios;
extern int nouveau_ignorelid;
extern int nouveau_nofbaccel;
extern int nouveau_noaccel;
+extern int nouveau_force_post;
extern int nouveau_override_conntype;
+extern char *nouveau_perflvl;
+extern int nouveau_perflvl_wr;
extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state);
extern int nouveau_pci_resume(struct pci_dev *pdev);
@@ -704,8 +791,10 @@ extern bool nouveau_wait_for_idle(struct drm_device *);
extern int nouveau_card_init(struct drm_device *);
/* nouveau_mem.c */
-extern int nouveau_mem_detect(struct drm_device *dev);
-extern int nouveau_mem_init(struct drm_device *);
+extern int nouveau_mem_vram_init(struct drm_device *);
+extern void nouveau_mem_vram_fini(struct drm_device *);
+extern int nouveau_mem_gart_init(struct drm_device *);
+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 *);
@@ -749,7 +838,6 @@ extern void nouveau_channel_free(struct nouveau_channel *);
extern int nouveau_gpuobj_early_init(struct drm_device *);
extern int nouveau_gpuobj_init(struct drm_device *);
extern void nouveau_gpuobj_takedown(struct drm_device *);
-extern void nouveau_gpuobj_late_takedown(struct drm_device *);
extern int nouveau_gpuobj_suspend(struct drm_device *dev);
extern void nouveau_gpuobj_suspend_cleanup(struct drm_device *dev);
extern void nouveau_gpuobj_resume(struct drm_device *dev);
@@ -759,24 +847,11 @@ extern void nouveau_gpuobj_channel_takedown(struct nouveau_channel *);
extern int nouveau_gpuobj_new(struct drm_device *, struct nouveau_channel *,
uint32_t size, int align, uint32_t flags,
struct nouveau_gpuobj **);
-extern int nouveau_gpuobj_del(struct drm_device *, struct nouveau_gpuobj **);
-extern int nouveau_gpuobj_ref_add(struct drm_device *, struct nouveau_channel *,
- uint32_t handle, struct nouveau_gpuobj *,
- struct nouveau_gpuobj_ref **);
-extern int nouveau_gpuobj_ref_del(struct drm_device *,
- struct nouveau_gpuobj_ref **);
-extern int nouveau_gpuobj_ref_find(struct nouveau_channel *, uint32_t handle,
- struct nouveau_gpuobj_ref **ref_ret);
-extern int nouveau_gpuobj_new_ref(struct drm_device *,
- struct nouveau_channel *alloc_chan,
- struct nouveau_channel *ref_chan,
- uint32_t handle, uint32_t size, int align,
- uint32_t flags, struct nouveau_gpuobj_ref **);
-extern int nouveau_gpuobj_new_fake(struct drm_device *,
- uint32_t p_offset, uint32_t b_offset,
- uint32_t size, uint32_t flags,
- struct nouveau_gpuobj **,
- struct nouveau_gpuobj_ref**);
+extern void nouveau_gpuobj_ref(struct nouveau_gpuobj *,
+ struct nouveau_gpuobj **);
+extern int nouveau_gpuobj_new_fake(struct drm_device *, u32 pinst, u64 vinst,
+ u32 size, u32 flags,
+ struct nouveau_gpuobj **);
extern int nouveau_gpuobj_dma_new(struct nouveau_channel *, int class,
uint64_t offset, uint64_t size, int access,
int target, struct nouveau_gpuobj **);
@@ -879,6 +954,7 @@ extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *,
enum dcb_gpio_tag);
extern struct dcb_connector_table_entry *
nouveau_bios_connector_entry(struct drm_device *, int index);
+extern u32 get_pll_register(struct drm_device *, enum pll_types);
extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
struct pll_lims *);
extern int nouveau_bios_run_display_table(struct drm_device *,
@@ -925,10 +1001,10 @@ extern int nv40_fb_init(struct drm_device *);
extern void nv40_fb_takedown(struct drm_device *);
extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t,
uint32_t, uint32_t);
-
/* nv50_fb.c */
extern int nv50_fb_init(struct drm_device *);
extern void nv50_fb_takedown(struct drm_device *);
+extern void nv50_fb_vm_trap(struct drm_device *, int display, const char *);
/* nvc0_fb.c */
extern int nvc0_fb_init(struct drm_device *);
@@ -939,7 +1015,6 @@ extern int nv04_fifo_init(struct drm_device *);
extern void nv04_fifo_disable(struct drm_device *);
extern void nv04_fifo_enable(struct drm_device *);
extern bool nv04_fifo_reassign(struct drm_device *, bool);
-extern bool nv04_fifo_cache_flush(struct drm_device *);
extern bool nv04_fifo_cache_pull(struct drm_device *, bool);
extern int nv04_fifo_channel_id(struct drm_device *);
extern int nv04_fifo_create_context(struct nouveau_channel *);
@@ -977,7 +1052,6 @@ extern void nvc0_fifo_takedown(struct drm_device *);
extern void nvc0_fifo_disable(struct drm_device *);
extern void nvc0_fifo_enable(struct drm_device *);
extern bool nvc0_fifo_reassign(struct drm_device *, bool);
-extern bool nvc0_fifo_cache_flush(struct drm_device *);
extern bool nvc0_fifo_cache_pull(struct drm_device *, bool);
extern int nvc0_fifo_channel_id(struct drm_device *);
extern int nvc0_fifo_create_context(struct nouveau_channel *);
@@ -1169,15 +1243,21 @@ extern int nouveau_bo_sync_gpu(struct nouveau_bo *, struct nouveau_channel *);
/* nouveau_fence.c */
struct nouveau_fence;
-extern int nouveau_fence_init(struct nouveau_channel *);
-extern void nouveau_fence_fini(struct nouveau_channel *);
+extern int nouveau_fence_init(struct drm_device *);
+extern void nouveau_fence_fini(struct drm_device *);
+extern int nouveau_fence_channel_init(struct nouveau_channel *);
+extern void nouveau_fence_channel_fini(struct nouveau_channel *);
extern void nouveau_fence_update(struct nouveau_channel *);
extern int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **,
bool emit);
extern int nouveau_fence_emit(struct nouveau_fence *);
+extern void nouveau_fence_work(struct nouveau_fence *fence,
+ void (*work)(void *priv, bool signalled),
+ void *priv);
struct nouveau_channel *nouveau_fence_channel(struct nouveau_fence *);
extern bool nouveau_fence_signalled(void *obj, void *arg);
extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
+extern int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *);
extern int nouveau_fence_flush(void *obj, void *arg);
extern void nouveau_fence_unref(void **obj);
extern void *nouveau_fence_ref(void *obj);
@@ -1255,12 +1335,11 @@ static inline void nv_wr32(struct drm_device *dev, unsigned reg, u32 val)
iowrite32_native(val, dev_priv->mmio + reg);
}
-static inline void nv_mask(struct drm_device *dev, u32 reg, u32 mask, u32 val)
+static inline u32 nv_mask(struct drm_device *dev, u32 reg, u32 mask, u32 val)
{
u32 tmp = nv_rd32(dev, reg);
- tmp &= ~mask;
- tmp |= val;
- nv_wr32(dev, reg, tmp);
+ nv_wr32(dev, reg, (tmp & ~mask) | val);
+ return tmp;
}
static inline u8 nv_rd08(struct drm_device *dev, unsigned reg)
@@ -1275,7 +1354,7 @@ static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val)
iowrite8(val, dev_priv->mmio + reg);
}
-#define nv_wait(reg, mask, val) \
+#define nv_wait(dev, reg, mask, val) \
nouveau_wait_until(dev, 2000000000ULL, (reg), (mask), (val))
/* PRAMIN access */
@@ -1292,17 +1371,8 @@ static inline void nv_wi32(struct drm_device *dev, unsigned offset, u32 val)
}
/* object access */
-static inline u32 nv_ro32(struct drm_device *dev, struct nouveau_gpuobj *obj,
- unsigned index)
-{
- return nv_ri32(dev, obj->im_pramin->start + index * 4);
-}
-
-static inline void nv_wo32(struct drm_device *dev, struct nouveau_gpuobj *obj,
- unsigned index, u32 val)
-{
- nv_wi32(dev, obj->im_pramin->start + index * 4, val);
-}
+extern u32 nv_ro32(struct nouveau_gpuobj *, u32 offset);
+extern void nv_wo32(struct nouveau_gpuobj *, u32 offset, u32 val);
/*
* Logging
@@ -1403,6 +1473,7 @@ nv_match_device(struct drm_device *dev, unsigned device,
#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 7c82d68bc155..ae69b61d93db 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -55,6 +55,7 @@ struct nouveau_encoder {
int dpcd_version;
int link_nr;
int link_bw;
+ bool enhanced_frame;
} dp;
};
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index dbd30b2e43fd..02a4d1fd4845 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -104,6 +104,8 @@ static struct fb_ops nouveau_fbcon_ops = {
.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,
};
static struct fb_ops nv04_fbcon_ops = {
@@ -117,6 +119,8 @@ static struct fb_ops nv04_fbcon_ops = {
.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,
};
static struct fb_ops nv50_fbcon_ops = {
@@ -130,6 +134,8 @@ static struct fb_ops nv50_fbcon_ops = {
.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,
};
static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 87ac21ec23d2..441b12420bb1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -28,9 +28,11 @@
#include "drm.h"
#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
#include "nouveau_dma.h"
-#define USE_REFCNT (dev_priv->card_type >= NV_10)
+#define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10)
+#define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17)
struct nouveau_fence {
struct nouveau_channel *channel;
@@ -39,6 +41,15 @@ struct nouveau_fence {
uint32_t sequence;
bool signalled;
+
+ void (*work)(void *priv, bool signalled);
+ void *priv;
+};
+
+struct nouveau_semaphore {
+ struct kref ref;
+ struct drm_device *dev;
+ struct drm_mm_node *mem;
};
static inline struct nouveau_fence *
@@ -59,14 +70,13 @@ nouveau_fence_del(struct kref *ref)
void
nouveau_fence_update(struct nouveau_channel *chan)
{
- struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
- struct list_head *entry, *tmp;
- struct nouveau_fence *fence;
+ struct drm_device *dev = chan->dev;
+ struct nouveau_fence *tmp, *fence;
uint32_t sequence;
spin_lock(&chan->fence.lock);
- if (USE_REFCNT)
+ if (USE_REFCNT(dev))
sequence = nvchan_rd32(chan, 0x48);
else
sequence = atomic_read(&chan->fence.last_sequence_irq);
@@ -75,12 +85,14 @@ nouveau_fence_update(struct nouveau_channel *chan)
goto out;
chan->fence.sequence_ack = sequence;
- list_for_each_safe(entry, tmp, &chan->fence.pending) {
- fence = list_entry(entry, struct nouveau_fence, entry);
-
+ list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
sequence = fence->sequence;
fence->signalled = true;
list_del(&fence->entry);
+
+ if (unlikely(fence->work))
+ fence->work(fence->priv, true);
+
kref_put(&fence->refcount, nouveau_fence_del);
if (sequence == chan->fence.sequence_ack)
@@ -121,8 +133,8 @@ nouveau_fence_channel(struct nouveau_fence *fence)
int
nouveau_fence_emit(struct nouveau_fence *fence)
{
- struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private;
struct nouveau_channel *chan = fence->channel;
+ struct drm_device *dev = chan->dev;
int ret;
ret = RING_SPACE(chan, 2);
@@ -143,7 +155,7 @@ nouveau_fence_emit(struct nouveau_fence *fence)
list_add_tail(&fence->entry, &chan->fence.pending);
spin_unlock(&chan->fence.lock);
- BEGIN_RING(chan, NvSubSw, USE_REFCNT ? 0x0050 : 0x0150, 1);
+ BEGIN_RING(chan, NvSubSw, USE_REFCNT(dev) ? 0x0050 : 0x0150, 1);
OUT_RING(chan, fence->sequence);
FIRE_RING(chan);
@@ -151,6 +163,25 @@ nouveau_fence_emit(struct nouveau_fence *fence)
}
void
+nouveau_fence_work(struct nouveau_fence *fence,
+ void (*work)(void *priv, bool signalled),
+ void *priv)
+{
+ BUG_ON(fence->work);
+
+ spin_lock(&fence->channel->fence.lock);
+
+ if (fence->signalled) {
+ work(priv, true);
+ } else {
+ fence->work = work;
+ fence->priv = priv;
+ }
+
+ spin_unlock(&fence->channel->fence.lock);
+}
+
+void
nouveau_fence_unref(void **sync_obj)
{
struct nouveau_fence *fence = nouveau_fence(*sync_obj);
@@ -213,6 +244,162 @@ nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
return ret;
}
+static struct nouveau_semaphore *
+alloc_semaphore(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_semaphore *sema;
+
+ if (!USE_SEMA(dev))
+ return NULL;
+
+ sema = kmalloc(sizeof(*sema), GFP_KERNEL);
+ if (!sema)
+ goto fail;
+
+ spin_lock(&dev_priv->fence.lock);
+ sema->mem = drm_mm_search_free(&dev_priv->fence.heap, 4, 0, 0);
+ if (sema->mem)
+ sema->mem = drm_mm_get_block(sema->mem, 4, 0);
+ spin_unlock(&dev_priv->fence.lock);
+
+ if (!sema->mem)
+ goto fail;
+
+ kref_init(&sema->ref);
+ sema->dev = dev;
+ nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 0);
+
+ return sema;
+fail:
+ kfree(sema);
+ return NULL;
+}
+
+static void
+free_semaphore(struct kref *ref)
+{
+ struct nouveau_semaphore *sema =
+ container_of(ref, struct nouveau_semaphore, ref);
+ struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
+
+ spin_lock(&dev_priv->fence.lock);
+ drm_mm_put_block(sema->mem);
+ spin_unlock(&dev_priv->fence.lock);
+
+ kfree(sema);
+}
+
+static void
+semaphore_work(void *priv, bool signalled)
+{
+ struct nouveau_semaphore *sema = priv;
+ struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
+
+ if (unlikely(!signalled))
+ nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 1);
+
+ kref_put(&sema->ref, free_semaphore);
+}
+
+static int
+emit_semaphore(struct nouveau_channel *chan, int method,
+ struct nouveau_semaphore *sema)
+{
+ struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
+ struct nouveau_fence *fence;
+ bool smart = (dev_priv->card_type >= NV_50);
+ int ret;
+
+ ret = RING_SPACE(chan, smart ? 8 : 4);
+ if (ret)
+ return ret;
+
+ if (smart) {
+ BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
+ OUT_RING(chan, NvSema);
+ }
+ BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_OFFSET, 1);
+ OUT_RING(chan, sema->mem->start);
+
+ if (smart && method == NV_SW_SEMAPHORE_ACQUIRE) {
+ /*
+ * NV50 tries to be too smart and context-switch
+ * between semaphores instead of doing a "first come,
+ * first served" strategy like previous cards
+ * do.
+ *
+ * That's bad because the ACQUIRE latency can get as
+ * large as the PFIFO context time slice in the
+ * typical DRI2 case where you have several
+ * outstanding semaphores at the same moment.
+ *
+ * If we're going to ACQUIRE, force the card to
+ * context switch before, just in case the matching
+ * RELEASE is already scheduled to be executed in
+ * another channel.
+ */
+ BEGIN_RING(chan, NvSubSw, NV_SW_YIELD, 1);
+ OUT_RING(chan, 0);
+ }
+
+ BEGIN_RING(chan, NvSubSw, method, 1);
+ OUT_RING(chan, 1);
+
+ if (smart && method == NV_SW_SEMAPHORE_RELEASE) {
+ /*
+ * Force the card to context switch, there may be
+ * another channel waiting for the semaphore we just
+ * released.
+ */
+ BEGIN_RING(chan, NvSubSw, NV_SW_YIELD, 1);
+ OUT_RING(chan, 0);
+ }
+
+ /* Delay semaphore destruction until its work is done */
+ ret = nouveau_fence_new(chan, &fence, true);
+ if (ret)
+ return ret;
+
+ kref_get(&sema->ref);
+ nouveau_fence_work(fence, semaphore_work, sema);
+ nouveau_fence_unref((void *)&fence);
+
+ return 0;
+}
+
+int
+nouveau_fence_sync(struct nouveau_fence *fence,
+ struct nouveau_channel *wchan)
+{
+ struct nouveau_channel *chan = nouveau_fence_channel(fence);
+ struct drm_device *dev = wchan->dev;
+ struct nouveau_semaphore *sema;
+ int ret;
+
+ if (likely(!fence || chan == wchan ||
+ nouveau_fence_signalled(fence, NULL)))
+ return 0;
+
+ sema = alloc_semaphore(dev);
+ if (!sema) {
+ /* Early card or broken userspace, fall back to
+ * software sync. */
+ return nouveau_fence_wait(fence, NULL, false, false);
+ }
+
+ /* Make wchan wait until it gets signalled */
+ ret = emit_semaphore(wchan, NV_SW_SEMAPHORE_ACQUIRE, sema);
+ if (ret)
+ goto out;
+
+ /* Signal the semaphore from chan */
+ ret = emit_semaphore(chan, NV_SW_SEMAPHORE_RELEASE, sema);
+out:
+ kref_put(&sema->ref, free_semaphore);
+ return ret;
+}
+
int
nouveau_fence_flush(void *sync_obj, void *sync_arg)
{
@@ -220,26 +407,123 @@ nouveau_fence_flush(void *sync_obj, void *sync_arg)
}
int
-nouveau_fence_init(struct nouveau_channel *chan)
+nouveau_fence_channel_init(struct nouveau_channel *chan)
{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *obj = NULL;
+ int ret;
+
+ /* Create an NV_SW object for various sync purposes */
+ ret = nouveau_gpuobj_sw_new(chan, NV_SW, &obj);
+ if (ret)
+ return ret;
+
+ ret = nouveau_ramht_insert(chan, NvSw, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ if (ret)
+ return ret;
+
+ ret = RING_SPACE(chan, 2);
+ if (ret)
+ return ret;
+ BEGIN_RING(chan, NvSubSw, 0, 1);
+ OUT_RING(chan, NvSw);
+
+ /* Create a DMA object for the shared cross-channel sync area. */
+ if (USE_SEMA(dev)) {
+ struct drm_mm_node *mem = dev_priv->fence.bo->bo.mem.mm_node;
+
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
+ mem->start << PAGE_SHIFT,
+ mem->size << PAGE_SHIFT,
+ NV_DMA_ACCESS_RW,
+ NV_DMA_TARGET_VIDMEM, &obj);
+ if (ret)
+ return ret;
+
+ ret = nouveau_ramht_insert(chan, NvSema, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ if (ret)
+ return ret;
+
+ ret = RING_SPACE(chan, 2);
+ if (ret)
+ return ret;
+ BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
+ OUT_RING(chan, NvSema);
+ }
+
+ FIRE_RING(chan);
+
INIT_LIST_HEAD(&chan->fence.pending);
spin_lock_init(&chan->fence.lock);
atomic_set(&chan->fence.last_sequence_irq, 0);
+
return 0;
}
void
-nouveau_fence_fini(struct nouveau_channel *chan)
+nouveau_fence_channel_fini(struct nouveau_channel *chan)
{
- struct list_head *entry, *tmp;
- struct nouveau_fence *fence;
-
- list_for_each_safe(entry, tmp, &chan->fence.pending) {
- fence = list_entry(entry, struct nouveau_fence, entry);
+ struct nouveau_fence *tmp, *fence;
+ list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
fence->signalled = true;
list_del(&fence->entry);
+
+ if (unlikely(fence->work))
+ fence->work(fence->priv, false);
+
kref_put(&fence->refcount, nouveau_fence_del);
}
}
+int
+nouveau_fence_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int ret;
+
+ /* Create a shared VRAM heap for cross-channel sync. */
+ if (USE_SEMA(dev)) {
+ ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM,
+ 0, 0, false, true, &dev_priv->fence.bo);
+ if (ret)
+ return ret;
+
+ ret = nouveau_bo_pin(dev_priv->fence.bo, TTM_PL_FLAG_VRAM);
+ if (ret)
+ goto fail;
+
+ ret = nouveau_bo_map(dev_priv->fence.bo);
+ if (ret)
+ goto fail;
+
+ ret = drm_mm_init(&dev_priv->fence.heap, 0,
+ dev_priv->fence.bo->bo.mem.size);
+ if (ret)
+ goto fail;
+
+ spin_lock_init(&dev_priv->fence.lock);
+ }
+
+ return 0;
+fail:
+ nouveau_bo_unmap(dev_priv->fence.bo);
+ nouveau_bo_ref(NULL, &dev_priv->fence.bo);
+ return ret;
+}
+
+void
+nouveau_fence_fini(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (USE_SEMA(dev)) {
+ drm_mm_takedown(&dev_priv->fence.heap);
+ nouveau_bo_unmap(dev_priv->fence.bo);
+ nouveau_bo_unpin(dev_priv->fence.bo);
+ nouveau_bo_ref(NULL, &dev_priv->fence.bo);
+ }
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 19620a6709f5..5c4c929d7f74 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -362,7 +362,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
list_for_each_entry(nvbo, list, entry) {
struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index];
- ret = nouveau_bo_sync_gpu(nvbo, chan);
+ ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan);
if (unlikely(ret)) {
NV_ERROR(dev, "fail pre-validate sync\n");
return ret;
@@ -385,7 +385,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
return ret;
}
- ret = nouveau_bo_sync_gpu(nvbo, chan);
+ ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan);
if (unlikely(ret)) {
NV_ERROR(dev, "fail post-validate sync\n");
return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.h b/drivers/gpu/drm/nouveau/nouveau_grctx.h
index 5d39c4ce8006..4a8ad1307fa4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_grctx.h
+++ b/drivers/gpu/drm/nouveau/nouveau_grctx.h
@@ -126,7 +126,7 @@ gr_def(struct nouveau_grctx *ctx, uint32_t reg, uint32_t val)
reg = (reg - 0x00400000) / 4;
reg = (reg - ctx->ctxprog_reg) + ctx->ctxvals_base;
- nv_wo32(ctx->dev, ctx->data, reg, val);
+ nv_wo32(ctx->data, reg * 4, val);
}
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
index 7b613682e400..bed669a54a2d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -305,7 +305,7 @@ setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg,
bool mpll = Preg == 0x4020;
uint32_t oldPval = nvReadMC(dev, Preg);
uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
- uint32_t Pval = (oldPval & (mpll ? ~(0x11 << 16) : ~(1 << 16))) |
+ uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
0xc << 28 | pv->log2P << 16;
uint32_t saved4600 = 0;
/* some cards have different maskc040s */
@@ -427,22 +427,12 @@ nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype,
struct nouveau_pll_vals *pllvals)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- const uint32_t nv04_regs[MAX_PLL_TYPES] = { NV_PRAMDAC_NVPLL_COEFF,
- NV_PRAMDAC_MPLL_COEFF,
- NV_PRAMDAC_VPLL_COEFF,
- NV_RAMDAC_VPLL2 };
- const uint32_t nv40_regs[MAX_PLL_TYPES] = { 0x4000,
- 0x4020,
- NV_PRAMDAC_VPLL_COEFF,
- NV_RAMDAC_VPLL2 };
- uint32_t reg1, pll1, pll2 = 0;
+ uint32_t reg1 = get_pll_register(dev, plltype), pll1, pll2 = 0;
struct pll_lims pll_lim;
int ret;
- if (dev_priv->card_type < NV_40)
- reg1 = nv04_regs[plltype];
- else
- reg1 = nv40_regs[plltype];
+ if (reg1 == 0)
+ return -ENOENT;
pll1 = nvReadMC(dev, reg1);
@@ -491,8 +481,10 @@ int
nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype)
{
struct nouveau_pll_vals pllvals;
+ int ret;
- if (plltype == MPLL && (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) {
+ if (plltype == PLL_MEMORY &&
+ (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) {
uint32_t mpllP;
pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
@@ -501,14 +493,17 @@ nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype)
return 400000 / mpllP;
} else
- if (plltype == MPLL && (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) {
+ if (plltype == PLL_MEMORY &&
+ (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) {
uint32_t clock;
pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
return clock;
}
- nouveau_hw_get_pllvals(dev, plltype, &pllvals);
+ ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals);
+ if (ret)
+ return ret;
return nouveau_hw_pllvals_to_clk(&pllvals);
}
@@ -526,9 +521,9 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
struct nouveau_pll_vals pv;
uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
- if (get_pll_limits(dev, head ? VPLL2 : VPLL1, &pll_lim))
+ if (get_pll_limits(dev, pllreg, &pll_lim))
return;
- nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &pv);
+ nouveau_hw_get_pllvals(dev, pllreg, &pv);
if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m &&
pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n &&
@@ -661,7 +656,7 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
if (dev_priv->card_type >= NV_10)
regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC);
- nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &regp->pllvals);
+ nouveau_hw_get_pllvals(dev, head ? PLL_VPLL1 : PLL_VPLL0, &regp->pllvals);
state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT);
if (nv_two_heads(dev))
state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
@@ -866,10 +861,11 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
- if (dev_priv->card_type >= NV_30) {
+ if (dev_priv->card_type >= NV_20)
rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
+
+ if (dev_priv->card_type >= NV_30)
rd_cio_state(dev, head, regp, 0x9f);
- }
rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
@@ -976,10 +972,11 @@ nv_load_state_ext(struct drm_device *dev, int head,
wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
- if (dev_priv->card_type >= NV_30) {
+ if (dev_priv->card_type >= NV_20)
wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
+
+ if (dev_priv->card_type >= NV_30)
wr_cio_state(dev, head, regp, 0x9f);
- }
wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index 84614858728b..fdd7e3de79c8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -299,7 +299,10 @@ nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr)
int
nouveau_i2c_identify(struct drm_device *dev, const char *what,
- struct i2c_board_info *info, int index)
+ struct i2c_board_info *info,
+ bool (*match)(struct nouveau_i2c_chan *,
+ struct i2c_board_info *),
+ int index)
{
struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index);
int i;
@@ -307,7 +310,8 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what,
NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index);
for (i = 0; info[i].addr; i++) {
- if (nouveau_probe_i2c_addr(i2c, info[i].addr)) {
+ if (nouveau_probe_i2c_addr(i2c, info[i].addr) &&
+ (!match || match(i2c, &info[i]))) {
NV_INFO(dev, "Detected %s: %s\n", what, info[i].type);
return i;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h
index cfe7c8426d1d..422b62fd8272 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.h
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
@@ -43,7 +43,10 @@ void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr);
int nouveau_i2c_identify(struct drm_device *dev, const char *what,
- struct i2c_board_info *info, int index);
+ struct i2c_board_info *info,
+ bool (*match)(struct nouveau_i2c_chan *,
+ struct i2c_board_info *),
+ int index);
extern const struct i2c_algorithm nouveau_dp_i2c_algo;
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
index 794b0ee30cf6..6fd51a51c608 100644
--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -35,6 +35,7 @@
#include "nouveau_drm.h"
#include "nouveau_drv.h"
#include "nouveau_reg.h"
+#include "nouveau_ramht.h"
#include <linux/ratelimit.h>
/* needed for hotplug irq */
@@ -106,15 +107,16 @@ nouveau_fifo_swmthd(struct nouveau_channel *chan, uint32_t addr, uint32_t data)
const int mthd = addr & 0x1ffc;
if (mthd == 0x0000) {
- struct nouveau_gpuobj_ref *ref = NULL;
+ struct nouveau_gpuobj *gpuobj;
- if (nouveau_gpuobj_ref_find(chan, data, &ref))
+ gpuobj = nouveau_ramht_find(chan, data);
+ if (!gpuobj)
return false;
- if (ref->gpuobj->engine != NVOBJ_ENGINE_SW)
+ if (gpuobj->engine != NVOBJ_ENGINE_SW)
return false;
- chan->sw_subchannel[subc] = ref->gpuobj->class;
+ chan->sw_subchannel[subc] = gpuobj->class;
nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_rd32(dev,
NV04_PFIFO_CACHE1_ENGINE) & ~(0xf << subc * 4));
return true;
@@ -200,16 +202,45 @@ nouveau_fifo_irq_handler(struct drm_device *dev)
}
if (status & NV_PFIFO_INTR_DMA_PUSHER) {
- NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d\n", chid);
+ u32 get = nv_rd32(dev, 0x003244);
+ u32 put = nv_rd32(dev, 0x003240);
+ u32 push = nv_rd32(dev, 0x003220);
+ u32 state = nv_rd32(dev, 0x003228);
+
+ if (dev_priv->card_type == NV_50) {
+ u32 ho_get = nv_rd32(dev, 0x003328);
+ u32 ho_put = nv_rd32(dev, 0x003320);
+ u32 ib_get = nv_rd32(dev, 0x003334);
+ u32 ib_put = nv_rd32(dev, 0x003330);
+
+ NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%02x%08x "
+ "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x "
+ "State 0x%08x Push 0x%08x\n",
+ chid, ho_get, get, ho_put, put, ib_get, ib_put,
+ state, push);
+
+ /* METHOD_COUNT, in DMA_STATE on earlier chipsets */
+ nv_wr32(dev, 0x003364, 0x00000000);
+ if (get != put || ho_get != ho_put) {
+ nv_wr32(dev, 0x003244, put);
+ nv_wr32(dev, 0x003328, ho_put);
+ } else
+ if (ib_get != ib_put) {
+ nv_wr32(dev, 0x003334, ib_put);
+ }
+ } else {
+ NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%08x "
+ "Put 0x%08x State 0x%08x Push 0x%08x\n",
+ chid, get, put, state, push);
- status &= ~NV_PFIFO_INTR_DMA_PUSHER;
- nv_wr32(dev, NV03_PFIFO_INTR_0,
- NV_PFIFO_INTR_DMA_PUSHER);
+ if (get != put)
+ nv_wr32(dev, 0x003244, put);
+ }
- nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, 0x00000000);
- if (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT) != get)
- nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET,
- get + 4);
+ nv_wr32(dev, 0x003228, 0x00000000);
+ nv_wr32(dev, 0x003220, 0x00000001);
+ nv_wr32(dev, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+ status &= ~NV_PFIFO_INTR_DMA_PUSHER;
}
if (status & NV_PFIFO_INTR_SEMAPHORE) {
@@ -226,6 +257,14 @@ nouveau_fifo_irq_handler(struct drm_device *dev)
nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
}
+ if (dev_priv->card_type == NV_50) {
+ if (status & 0x00000010) {
+ nv50_fb_vm_trap(dev, 1, "PFIFO_BAR_FAULT");
+ status &= ~0x00000010;
+ nv_wr32(dev, 0x002100, 0x00000010);
+ }
+ }
+
if (status) {
NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n",
status, chid);
@@ -357,7 +396,7 @@ nouveau_graph_chid_from_grctx(struct drm_device *dev)
if (!chan || !chan->ramin_grctx)
continue;
- if (inst == chan->ramin_grctx->instance)
+ if (inst == chan->ramin_grctx->pinst)
break;
}
} else {
@@ -369,7 +408,7 @@ nouveau_graph_chid_from_grctx(struct drm_device *dev)
if (!chan || !chan->ramin)
continue;
- if (inst == chan->ramin->instance)
+ if (inst == chan->ramin->vinst)
break;
}
}
@@ -605,40 +644,6 @@ nouveau_pgraph_irq_handler(struct drm_device *dev)
nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
}
-static void
-nv50_pfb_vm_trap(struct drm_device *dev, int display, const char *name)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t trap[6];
- int i, ch;
- uint32_t idx = nv_rd32(dev, 0x100c90);
- if (idx & 0x80000000) {
- idx &= 0xffffff;
- if (display) {
- for (i = 0; i < 6; i++) {
- nv_wr32(dev, 0x100c90, idx | i << 24);
- trap[i] = nv_rd32(dev, 0x100c94);
- }
- for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) {
- struct nouveau_channel *chan = dev_priv->fifos[ch];
-
- if (!chan || !chan->ramin)
- continue;
-
- if (trap[1] == chan->ramin->instance >> 12)
- break;
- }
- NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x %08x channel %d\n",
- name, (trap[5]&0x100?"read":"write"),
- trap[5]&0xff, trap[4]&0xffff,
- trap[3]&0xffff, trap[0], trap[2], ch);
- }
- nv_wr32(dev, 0x100c90, idx | 0x80000000);
- } else if (display) {
- NV_INFO(dev, "%s - no VM fault?\n", name);
- }
-}
-
static struct nouveau_enum_names nv50_mp_exec_error_names[] =
{
{ 3, "STACK_UNDERFLOW" },
@@ -711,7 +716,7 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old,
tps++;
switch (type) {
case 6: /* texture error... unknown for now */
- nv50_pfb_vm_trap(dev, display, name);
+ nv50_fb_vm_trap(dev, display, name);
if (display) {
NV_ERROR(dev, "magic set %d:\n", i);
for (r = ustatus_addr + 4; r <= ustatus_addr + 0x10; r += 4)
@@ -734,7 +739,7 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old,
uint32_t e1c = nv_rd32(dev, ustatus_addr + 0x14);
uint32_t e20 = nv_rd32(dev, ustatus_addr + 0x18);
uint32_t e24 = nv_rd32(dev, ustatus_addr + 0x1c);
- nv50_pfb_vm_trap(dev, display, name);
+ nv50_fb_vm_trap(dev, display, name);
/* 2d engine destination */
if (ustatus & 0x00000010) {
if (display) {
@@ -817,7 +822,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev)
/* Known to be triggered by screwed up NOTIFY and COND... */
if (ustatus & 0x00000001) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_FAULT");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_FAULT");
nv_wr32(dev, 0x400500, 0);
if (nv_rd32(dev, 0x400808) & 0x80000000) {
if (display) {
@@ -842,7 +847,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev)
ustatus &= ~0x00000001;
}
if (ustatus & 0x00000002) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_QUERY");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_QUERY");
nv_wr32(dev, 0x400500, 0);
if (nv_rd32(dev, 0x40084c) & 0x80000000) {
if (display) {
@@ -884,15 +889,15 @@ nv50_pgraph_trap_handler(struct drm_device *dev)
NV_INFO(dev, "PGRAPH_TRAP_M2MF - no ustatus?\n");
}
if (ustatus & 0x00000001) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_NOTIFY");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_NOTIFY");
ustatus &= ~0x00000001;
}
if (ustatus & 0x00000002) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_IN");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_IN");
ustatus &= ~0x00000002;
}
if (ustatus & 0x00000004) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_OUT");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_OUT");
ustatus &= ~0x00000004;
}
NV_INFO (dev, "PGRAPH_TRAP_M2MF - %08x %08x %08x %08x\n",
@@ -917,7 +922,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev)
NV_INFO(dev, "PGRAPH_TRAP_VFETCH - no ustatus?\n");
}
if (ustatus & 0x00000001) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_VFETCH_FAULT");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_VFETCH_FAULT");
NV_INFO (dev, "PGRAPH_TRAP_VFETCH_FAULT - %08x %08x %08x %08x\n",
nv_rd32(dev, 0x400c00),
nv_rd32(dev, 0x400c08),
@@ -939,7 +944,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev)
NV_INFO(dev, "PGRAPH_TRAP_STRMOUT - no ustatus?\n");
}
if (ustatus & 0x00000001) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_STRMOUT_FAULT");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_STRMOUT_FAULT");
NV_INFO (dev, "PGRAPH_TRAP_STRMOUT_FAULT - %08x %08x %08x %08x\n",
nv_rd32(dev, 0x401804),
nv_rd32(dev, 0x401808),
@@ -964,7 +969,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev)
NV_INFO(dev, "PGRAPH_TRAP_CCACHE - no ustatus?\n");
}
if (ustatus & 0x00000001) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_CCACHE_FAULT");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_CCACHE_FAULT");
NV_INFO (dev, "PGRAPH_TRAP_CCACHE_FAULT - %08x %08x %08x %08x %08x %08x %08x\n",
nv_rd32(dev, 0x405800),
nv_rd32(dev, 0x405804),
@@ -986,7 +991,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev)
* remaining, so try to handle it anyway. Perhaps related to that
* unknown DMA slot on tesla? */
if (status & 0x20) {
- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_UNKC04");
+ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_UNKC04");
ustatus = nv_rd32(dev, 0x402000) & 0x7fffffff;
if (display)
NV_INFO(dev, "PGRAPH_TRAP_UNKC04 - Unhandled ustatus 0x%08x\n", ustatus);
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 9689d4147686..a163c7c612e7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -35,6 +35,8 @@
#include "drm_sarea.h"
#include "nouveau_drv.h"
+#define MIN(a,b) a < b ? a : b
+
/*
* NV10-NV40 tiling helpers
*/
@@ -47,18 +49,14 @@ nv10_mem_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+ struct nouveau_tile_reg *tile = &dev_priv->tile[i];
tile->addr = addr;
tile->size = size;
tile->used = !!pitch;
nouveau_fence_unref((void **)&tile->fence);
- if (!pfifo->cache_flush(dev))
- return;
-
pfifo->reassign(dev, false);
- pfifo->cache_flush(dev);
pfifo->cache_pull(dev, false);
nouveau_wait_for_idle(dev);
@@ -76,34 +74,36 @@ nv10_mem_set_tiling(struct drm_device *dev, uint32_t addr, uint32_t size,
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
- struct nouveau_tile_reg *tile = dev_priv->tile.reg, *found = NULL;
- int i;
+ struct nouveau_tile_reg *found = NULL;
+ unsigned long i, flags;
- spin_lock(&dev_priv->tile.lock);
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
for (i = 0; i < pfb->num_tiles; i++) {
- if (tile[i].used)
+ struct nouveau_tile_reg *tile = &dev_priv->tile[i];
+
+ if (tile->used)
/* Tile region in use. */
continue;
- if (tile[i].fence &&
- !nouveau_fence_signalled(tile[i].fence, NULL))
+ if (tile->fence &&
+ !nouveau_fence_signalled(tile->fence, NULL))
/* Pending tile region. */
continue;
- if (max(tile[i].addr, addr) <
- min(tile[i].addr + tile[i].size, addr + size))
+ if (max(tile->addr, addr) <
+ min(tile->addr + tile->size, addr + size))
/* Kill an intersecting tile region. */
nv10_mem_set_region_tiling(dev, i, 0, 0, 0);
if (pitch && !found) {
/* Free tile region. */
nv10_mem_set_region_tiling(dev, i, addr, size, pitch);
- found = &tile[i];
+ found = tile;
}
}
- spin_unlock(&dev_priv->tile.lock);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
return found;
}
@@ -169,8 +169,9 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
virt += (end - pte);
while (pte < end) {
- nv_wo32(dev, pgt, pte++, offset_l);
- nv_wo32(dev, pgt, pte++, offset_h);
+ nv_wo32(pgt, (pte * 4) + 0, offset_l);
+ nv_wo32(pgt, (pte * 4) + 4, offset_h);
+ pte += 2;
}
}
}
@@ -203,8 +204,10 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
pages -= (end - pte);
virt += (end - pte) << 15;
- while (pte < end)
- nv_wo32(dev, pgt, pte++, 0);
+ while (pte < end) {
+ nv_wo32(pgt, (pte * 4), 0);
+ pte++;
+ }
}
dev_priv->engine.instmem.flush(dev);
@@ -218,7 +221,7 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
* Cleanup everything
*/
void
-nouveau_mem_close(struct drm_device *dev)
+nouveau_mem_vram_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -229,6 +232,19 @@ nouveau_mem_close(struct drm_device *dev)
nouveau_ttm_global_release(dev_priv);
+ if (dev_priv->fb_mtrr >= 0) {
+ drm_mtrr_del(dev_priv->fb_mtrr,
+ pci_resource_start(dev->pdev, 1),
+ pci_resource_len(dev->pdev, 1), DRM_MTRR_WC);
+ dev_priv->fb_mtrr = -1;
+ }
+}
+
+void
+nouveau_mem_gart_fini(struct drm_device *dev)
+{
+ nouveau_sgdma_takedown(dev);
+
if (drm_core_has_AGP(dev) && dev->agp) {
struct drm_agp_mem *entry, *tempe;
@@ -248,13 +264,6 @@ nouveau_mem_close(struct drm_device *dev)
dev->agp->acquired = 0;
dev->agp->enabled = 0;
}
-
- if (dev_priv->fb_mtrr) {
- drm_mtrr_del(dev_priv->fb_mtrr,
- pci_resource_start(dev->pdev, 1),
- pci_resource_len(dev->pdev, 1), DRM_MTRR_WC);
- dev_priv->fb_mtrr = -1;
- }
}
static uint32_t
@@ -305,8 +314,62 @@ nouveau_mem_detect_nforce(struct drm_device *dev)
return 0;
}
-/* returns the amount of FB ram in bytes */
-int
+static void
+nv50_vram_preinit(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int i, parts, colbits, rowbitsa, rowbitsb, banks;
+ u64 rowsize, predicted;
+ u32 r0, r4, rt, ru;
+
+ r0 = nv_rd32(dev, 0x100200);
+ r4 = nv_rd32(dev, 0x100204);
+ rt = nv_rd32(dev, 0x100250);
+ ru = nv_rd32(dev, 0x001540);
+ NV_DEBUG(dev, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
+
+ for (i = 0, parts = 0; i < 8; i++) {
+ if (ru & (0x00010000 << i))
+ parts++;
+ }
+
+ colbits = (r4 & 0x0000f000) >> 12;
+ rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
+ rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
+ banks = ((r4 & 0x01000000) ? 8 : 4);
+
+ rowsize = parts * banks * (1 << colbits) * 8;
+ predicted = rowsize << rowbitsa;
+ if (r0 & 0x00000004)
+ predicted += rowsize << rowbitsb;
+
+ if (predicted != dev_priv->vram_size) {
+ NV_WARN(dev, "memory controller reports %dMiB VRAM\n",
+ (u32)(dev_priv->vram_size >> 20));
+ NV_WARN(dev, "we calculated %dMiB VRAM\n",
+ (u32)(predicted >> 20));
+ }
+
+ dev_priv->vram_rblock_size = rowsize >> 12;
+ if (rt & 1)
+ dev_priv->vram_rblock_size *= 3;
+
+ NV_DEBUG(dev, "rblock %lld bytes\n",
+ (u64)dev_priv->vram_rblock_size << 12);
+}
+
+static void
+nvaa_vram_preinit(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ /* To our knowledge, there's no large scale reordering of pages
+ * that occurs on IGP chipsets.
+ */
+ dev_priv->vram_rblock_size = 1;
+}
+
+static int
nouveau_mem_detect(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -325,9 +388,18 @@ nouveau_mem_detect(struct drm_device *dev)
dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
dev_priv->vram_size &= 0xffffffff00ll;
- if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) {
+
+ switch (dev_priv->chipset) {
+ case 0xaa:
+ case 0xac:
+ case 0xaf:
dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10);
dev_priv->vram_sys_base <<= 12;
+ nvaa_vram_preinit(dev);
+ break;
+ default:
+ nv50_vram_preinit(dev);
+ break;
}
} else {
dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20;
@@ -345,6 +417,33 @@ nouveau_mem_detect(struct drm_device *dev)
return -ENOMEM;
}
+#if __OS_HAS_AGP
+static unsigned long
+get_agp_mode(struct drm_device *dev, unsigned long mode)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ /*
+ * FW seems to be broken on nv18, it makes the card lock up
+ * randomly.
+ */
+ if (dev_priv->chipset == 0x18)
+ mode &= ~PCI_AGP_COMMAND_FW;
+
+ /*
+ * AGP mode set in the command line.
+ */
+ if (nouveau_agpmode > 0) {
+ bool agpv3 = mode & 0x8;
+ int rate = agpv3 ? nouveau_agpmode / 4 : nouveau_agpmode;
+
+ mode = (mode & ~0x7) | (rate & 0x7);
+ }
+
+ return mode;
+}
+#endif
+
int
nouveau_mem_reset_agp(struct drm_device *dev)
{
@@ -355,7 +454,8 @@ nouveau_mem_reset_agp(struct drm_device *dev)
/* First of all, disable fast writes, otherwise if it's
* already enabled in the AGP bridge and we disable the card's
* AGP controller we might be locking ourselves out of it. */
- if (nv_rd32(dev, NV04_PBUS_PCI_NV_19) & PCI_AGP_COMMAND_FW) {
+ if ((nv_rd32(dev, NV04_PBUS_PCI_NV_19) |
+ dev->agp->mode) & PCI_AGP_COMMAND_FW) {
struct drm_agp_info info;
struct drm_agp_mode mode;
@@ -363,7 +463,7 @@ nouveau_mem_reset_agp(struct drm_device *dev)
if (ret)
return ret;
- mode.mode = info.mode & ~PCI_AGP_COMMAND_FW;
+ mode.mode = get_agp_mode(dev, info.mode) & ~PCI_AGP_COMMAND_FW;
ret = drm_agp_enable(dev, mode);
if (ret)
return ret;
@@ -418,7 +518,7 @@ nouveau_mem_init_agp(struct drm_device *dev)
}
/* see agp.h for the AGPSTAT_* modes available */
- mode.mode = info.mode;
+ mode.mode = get_agp_mode(dev, info.mode);
ret = drm_agp_enable(dev, mode);
if (ret) {
NV_ERROR(dev, "Unable to enable AGP: %d\n", ret);
@@ -433,24 +533,27 @@ nouveau_mem_init_agp(struct drm_device *dev)
}
int
-nouveau_mem_init(struct drm_device *dev)
+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;
- int ret, dma_bits = 32;
-
- dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
- dev_priv->gart_info.type = NOUVEAU_GART_NONE;
+ int ret, dma_bits;
if (dev_priv->card_type >= NV_50 &&
pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
dma_bits = 40;
+ else
+ dma_bits = 32;
ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits));
- if (ret) {
- NV_ERROR(dev, "Error setting DMA mask: %d\n", ret);
+ if (ret)
return ret;
- }
+
+ ret = nouveau_mem_detect(dev);
+ if (ret)
+ return ret;
+
+ dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
ret = nouveau_ttm_global_init(dev_priv);
if (ret)
@@ -465,8 +568,6 @@ nouveau_mem_init(struct drm_device *dev)
return ret;
}
- spin_lock_init(&dev_priv->tile.lock);
-
dev_priv->fb_available_size = dev_priv->vram_size;
dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
if (dev_priv->fb_mappable_pages > pci_resource_len(dev->pdev, 1))
@@ -474,7 +575,16 @@ nouveau_mem_init(struct drm_device *dev)
pci_resource_len(dev->pdev, 1);
dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
- /* remove reserved space at end of vram from available amount */
+ /* reserve space at end of VRAM for PRAMIN */
+ if (dev_priv->chipset == 0x40 || dev_priv->chipset == 0x47 ||
+ dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b)
+ dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
+ else
+ if (dev_priv->card_type >= NV_40)
+ dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
+ else
+ dev_priv->ramin_rsvd_vram = (512 * 1024);
+
dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
dev_priv->fb_aper_free = dev_priv->fb_available_size;
@@ -495,9 +605,23 @@ nouveau_mem_init(struct drm_device *dev)
nouveau_bo_ref(NULL, &dev_priv->vga_ram);
}
- /* GART */
+ dev_priv->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1),
+ pci_resource_len(dev->pdev, 1),
+ DRM_MTRR_WC);
+ return 0;
+}
+
+int
+nouveau_mem_gart_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
+ int ret;
+
+ dev_priv->gart_info.type = NOUVEAU_GART_NONE;
+
#if !defined(__powerpc__) && !defined(__ia64__)
- if (drm_device_is_agp(dev) && dev->agp && !nouveau_noagp) {
+ if (drm_device_is_agp(dev) && dev->agp && nouveau_agpmode) {
ret = nouveau_mem_init_agp(dev);
if (ret)
NV_ERROR(dev, "Error initialising AGP: %d\n", ret);
@@ -523,11 +647,150 @@ nouveau_mem_init(struct drm_device *dev)
return ret;
}
- dev_priv->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1),
- pci_resource_len(dev->pdev, 1),
- DRM_MTRR_WC);
-
return 0;
}
+void
+nouveau_mem_timing_init(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_memtimings *memtimings = &pm->memtimings;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct bit_entry P;
+ u8 tUNK_0, tUNK_1, tUNK_2;
+ u8 tRP; /* Byte 3 */
+ u8 tRAS; /* Byte 5 */
+ u8 tRFC; /* Byte 7 */
+ u8 tRC; /* Byte 9 */
+ u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
+ u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
+ u8 *mem = NULL, *entry;
+ int i, recordlen, entries;
+
+ if (bios->type == NVBIOS_BIT) {
+ if (bit_table(dev, 'P', &P))
+ return;
+
+ if (P.version == 1)
+ mem = ROMPTR(bios, P.data[4]);
+ else
+ if (P.version == 2)
+ mem = ROMPTR(bios, P.data[8]);
+ else {
+ NV_WARN(dev, "unknown mem for BIT P %d\n", P.version);
+ }
+ } else {
+ NV_DEBUG(dev, "BMP version too old for memory\n");
+ return;
+ }
+
+ if (!mem) {
+ NV_DEBUG(dev, "memory timing table pointer invalid\n");
+ return;
+ }
+ if (mem[0] != 0x10) {
+ NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]);
+ return;
+ }
+
+ /* validate record length */
+ entries = mem[2];
+ recordlen = mem[3];
+ if (recordlen < 15) {
+ NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]);
+ return;
+ }
+
+ /* parse vbios entries into common format */
+ memtimings->timing =
+ kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
+ if (!memtimings->timing)
+ return;
+
+ entry = mem + mem[1];
+ for (i = 0; i < entries; i++, entry += recordlen) {
+ struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
+ if (entry[0] == 0)
+ continue;
+
+ tUNK_18 = 1;
+ tUNK_19 = 1;
+ tUNK_20 = 0;
+ tUNK_21 = 0;
+ switch (MIN(recordlen,21)) {
+ case 21:
+ tUNK_21 = entry[21];
+ case 20:
+ tUNK_20 = entry[20];
+ case 19:
+ tUNK_19 = entry[19];
+ case 18:
+ tUNK_18 = entry[18];
+ default:
+ tUNK_0 = entry[0];
+ tUNK_1 = entry[1];
+ tUNK_2 = entry[2];
+ tRP = entry[3];
+ tRAS = entry[5];
+ tRFC = entry[7];
+ tRC = entry[9];
+ tUNK_10 = entry[10];
+ tUNK_11 = entry[11];
+ tUNK_12 = entry[12];
+ tUNK_13 = entry[13];
+ tUNK_14 = entry[14];
+ break;
+ }
+
+ timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP);
+
+ /* XXX: I don't trust the -1's and +1's... they must come
+ * from somewhere! */
+ timing->reg_100224 = ((tUNK_0 + tUNK_19 + 1) << 24 |
+ tUNK_18 << 16 |
+ (tUNK_1 + tUNK_19 + 1) << 8 |
+ (tUNK_2 - 1));
+
+ timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
+ if(recordlen > 19) {
+ timing->reg_100228 += (tUNK_19 - 1) << 24;
+ } else {
+ timing->reg_100228 += tUNK_12 << 24;
+ }
+
+ /* XXX: reg_10022c */
+
+ timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
+ tUNK_13 << 8 | tUNK_13);
+
+ /* XXX: +6? */
+ timing->reg_100234 = (tRAS << 24 | (tUNK_19 + 6) << 8 | tRC);
+ if(tUNK_10 > tUNK_11) {
+ timing->reg_100234 += tUNK_10 << 16;
+ } else {
+ timing->reg_100234 += tUNK_11 << 16;
+ }
+
+ /* XXX; reg_100238, reg_10023c */
+ NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
+ timing->reg_100220, timing->reg_100224,
+ timing->reg_100228, timing->reg_10022c);
+ NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
+ timing->reg_100230, timing->reg_100234,
+ timing->reg_100238, timing->reg_10023c);
+ }
+
+ memtimings->nr_timing = entries;
+ memtimings->supported = true;
+}
+
+void
+nouveau_mem_timing_fini(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings;
+
+ kfree(mem->timing);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
index 3ec181ff50ce..2cc59f8c658b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_notifier.c
+++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
@@ -28,6 +28,7 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
int
nouveau_notifier_init_channel(struct nouveau_channel *chan)
@@ -112,7 +113,7 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
return -ENOMEM;
}
- offset = chan->notifier_bo->bo.mem.mm_node->start << PAGE_SHIFT;
+ offset = chan->notifier_bo->bo.mem.start << PAGE_SHIFT;
if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) {
target = NV_DMA_TARGET_VIDMEM;
} else
@@ -146,11 +147,11 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
nobj->dtor = nouveau_notifier_gpuobj_dtor;
nobj->priv = mem;
- ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL);
+ ret = nouveau_ramht_insert(chan, handle, nobj);
+ nouveau_gpuobj_ref(NULL, &nobj);
if (ret) {
- nouveau_gpuobj_del(dev, &nobj);
drm_mm_put_block(mem);
- NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret);
+ NV_ERROR(dev, "Error adding notifier to ramht: %d\n", ret);
return ret;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
index b6bcb254f4ab..896cf8634144 100644
--- a/drivers/gpu/drm/nouveau/nouveau_object.c
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
@@ -34,6 +34,7 @@
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
+#include "nouveau_ramht.h"
/* NVidia uses context objects to drive drawing operations.
@@ -65,137 +66,6 @@
The key into the hash table depends on the object handle and channel id and
is given as:
*/
-static uint32_t
-nouveau_ramht_hash_handle(struct drm_device *dev, int channel, uint32_t handle)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t hash = 0;
- int i;
-
- NV_DEBUG(dev, "ch%d handle=0x%08x\n", channel, handle);
-
- for (i = 32; i > 0; i -= dev_priv->ramht_bits) {
- hash ^= (handle & ((1 << dev_priv->ramht_bits) - 1));
- handle >>= dev_priv->ramht_bits;
- }
-
- if (dev_priv->card_type < NV_50)
- hash ^= channel << (dev_priv->ramht_bits - 4);
- hash <<= 3;
-
- NV_DEBUG(dev, "hash=0x%08x\n", hash);
- return hash;
-}
-
-static int
-nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
- uint32_t offset)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t ctx = nv_ro32(dev, ramht, (offset + 4)/4);
-
- if (dev_priv->card_type < NV_40)
- return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
- return (ctx != 0);
-}
-
-static int
-nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
- struct nouveau_channel *chan = ref->channel;
- struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
- uint32_t ctx, co, ho;
-
- if (!ramht) {
- NV_ERROR(dev, "No hash table!\n");
- return -EINVAL;
- }
-
- if (dev_priv->card_type < NV_40) {
- ctx = NV_RAMHT_CONTEXT_VALID | (ref->instance >> 4) |
- (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
- (ref->gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
- } else
- if (dev_priv->card_type < NV_50) {
- ctx = (ref->instance >> 4) |
- (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
- (ref->gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
- } else {
- if (ref->gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
- ctx = (ref->instance << 10) | 2;
- } else {
- ctx = (ref->instance >> 4) |
- ((ref->gpuobj->engine <<
- NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
- }
- }
-
- co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
- do {
- if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
- NV_DEBUG(dev,
- "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
- chan->id, co, ref->handle, ctx);
- nv_wo32(dev, ramht, (co + 0)/4, ref->handle);
- nv_wo32(dev, ramht, (co + 4)/4, ctx);
-
- list_add_tail(&ref->list, &chan->ramht_refs);
- instmem->flush(dev);
- return 0;
- }
- NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
- chan->id, co, nv_ro32(dev, ramht, co/4));
-
- co += 8;
- if (co >= dev_priv->ramht_size)
- co = 0;
- } while (co != ho);
-
- NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
- return -ENOMEM;
-}
-
-static void
-nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
- struct nouveau_channel *chan = ref->channel;
- struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
- uint32_t co, ho;
-
- if (!ramht) {
- NV_ERROR(dev, "No hash table!\n");
- return;
- }
-
- co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
- do {
- if (nouveau_ramht_entry_valid(dev, ramht, co) &&
- (ref->handle == nv_ro32(dev, ramht, (co/4)))) {
- NV_DEBUG(dev,
- "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
- chan->id, co, ref->handle,
- nv_ro32(dev, ramht, (co + 4)));
- nv_wo32(dev, ramht, (co + 0)/4, 0x00000000);
- nv_wo32(dev, ramht, (co + 4)/4, 0x00000000);
-
- list_del(&ref->list);
- instmem->flush(dev);
- return;
- }
-
- co += 8;
- if (co >= dev_priv->ramht_size)
- co = 0;
- } while (co != ho);
- list_del(&ref->list);
-
- NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
- chan->id, ref->handle);
-}
int
nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
@@ -205,7 +75,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
struct nouveau_gpuobj *gpuobj;
- struct drm_mm *pramin = NULL;
+ struct drm_mm_node *ramin = NULL;
int ret;
NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n",
@@ -218,69 +88,102 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
if (!gpuobj)
return -ENOMEM;
NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
+ gpuobj->dev = dev;
gpuobj->flags = flags;
- gpuobj->im_channel = chan;
+ kref_init(&gpuobj->refcount);
+ gpuobj->size = size;
+ spin_lock(&dev_priv->ramin_lock);
list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+ spin_unlock(&dev_priv->ramin_lock);
- /* Choose between global instmem heap, and per-channel private
- * instmem heap. On <NV50 allow requests for private instmem
- * to be satisfied from global heap if no per-channel area
- * available.
- */
if (chan) {
NV_DEBUG(dev, "channel heap\n");
- pramin = &chan->ramin_heap;
+
+ ramin = drm_mm_search_free(&chan->ramin_heap, size, align, 0);
+ if (ramin)
+ ramin = drm_mm_get_block(ramin, size, align);
+
+ if (!ramin) {
+ nouveau_gpuobj_ref(NULL, &gpuobj);
+ return -ENOMEM;
+ }
} else {
NV_DEBUG(dev, "global heap\n");
- pramin = &dev_priv->ramin_heap;
+ /* allocate backing pages, sets vinst */
ret = engine->instmem.populate(dev, gpuobj, &size);
if (ret) {
- nouveau_gpuobj_del(dev, &gpuobj);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
return ret;
}
- }
- /* Allocate a chunk of the PRAMIN aperture */
- gpuobj->im_pramin = drm_mm_search_free(pramin, size, align, 0);
- if (gpuobj->im_pramin)
- gpuobj->im_pramin = drm_mm_get_block(gpuobj->im_pramin, size, align);
+ /* try and get aperture space */
+ do {
+ if (drm_mm_pre_get(&dev_priv->ramin_heap))
+ return -ENOMEM;
+
+ spin_lock(&dev_priv->ramin_lock);
+ ramin = drm_mm_search_free(&dev_priv->ramin_heap, size,
+ align, 0);
+ if (ramin == NULL) {
+ spin_unlock(&dev_priv->ramin_lock);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
+ return ret;
+ }
- if (!gpuobj->im_pramin) {
- nouveau_gpuobj_del(dev, &gpuobj);
- return -ENOMEM;
+ ramin = drm_mm_get_block_atomic(ramin, size, align);
+ spin_unlock(&dev_priv->ramin_lock);
+ } while (ramin == NULL);
+
+ /* on nv50 it's ok to fail, we have a fallback path */
+ if (!ramin && dev_priv->card_type < NV_50) {
+ nouveau_gpuobj_ref(NULL, &gpuobj);
+ return -ENOMEM;
+ }
}
- if (!chan) {
+ /* if we got a chunk of the aperture, map pages into it */
+ gpuobj->im_pramin = ramin;
+ if (!chan && gpuobj->im_pramin && dev_priv->ramin_available) {
ret = engine->instmem.bind(dev, gpuobj);
if (ret) {
- nouveau_gpuobj_del(dev, &gpuobj);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
return ret;
}
}
+ /* calculate the various different addresses for the object */
+ if (chan) {
+ gpuobj->pinst = chan->ramin->pinst;
+ if (gpuobj->pinst != ~0)
+ gpuobj->pinst += gpuobj->im_pramin->start;
+
+ if (dev_priv->card_type < NV_50) {
+ gpuobj->cinst = gpuobj->pinst;
+ } else {
+ gpuobj->cinst = gpuobj->im_pramin->start;
+ gpuobj->vinst = gpuobj->im_pramin->start +
+ chan->ramin->vinst;
+ }
+ } else {
+ if (gpuobj->im_pramin)
+ gpuobj->pinst = gpuobj->im_pramin->start;
+ else
+ gpuobj->pinst = ~0;
+ gpuobj->cinst = 0xdeadbeef;
+ }
+
if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
int i;
- for (i = 0; i < gpuobj->im_pramin->size; i += 4)
- nv_wo32(dev, gpuobj, i/4, 0);
+ for (i = 0; i < gpuobj->size; i += 4)
+ nv_wo32(gpuobj, i, 0);
engine->instmem.flush(dev);
}
- *gpuobj_ret = gpuobj;
- return 0;
-}
-
-int
-nouveau_gpuobj_early_init(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- NV_DEBUG(dev, "\n");
-
- INIT_LIST_HEAD(&dev_priv->gpuobj_list);
+ *gpuobj_ret = gpuobj;
return 0;
}
@@ -288,18 +191,12 @@ int
nouveau_gpuobj_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- int ret;
NV_DEBUG(dev, "\n");
- if (dev_priv->card_type < NV_50) {
- ret = nouveau_gpuobj_new_fake(dev,
- dev_priv->ramht_offset, ~0, dev_priv->ramht_size,
- NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ALLOW_NO_REFS,
- &dev_priv->ramht, NULL);
- if (ret)
- return ret;
- }
+ INIT_LIST_HEAD(&dev_priv->gpuobj_list);
+ spin_lock_init(&dev_priv->ramin_lock);
+ dev_priv->ramin_base = ~0;
return 0;
}
@@ -311,297 +208,89 @@ nouveau_gpuobj_takedown(struct drm_device *dev)
NV_DEBUG(dev, "\n");
- nouveau_gpuobj_del(dev, &dev_priv->ramht);
+ BUG_ON(!list_empty(&dev_priv->gpuobj_list));
}
-void
-nouveau_gpuobj_late_takedown(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *gpuobj = NULL;
- struct list_head *entry, *tmp;
-
- NV_DEBUG(dev, "\n");
-
- list_for_each_safe(entry, tmp, &dev_priv->gpuobj_list) {
- gpuobj = list_entry(entry, struct nouveau_gpuobj, list);
-
- NV_ERROR(dev, "gpuobj %p still exists at takedown, refs=%d\n",
- gpuobj, gpuobj->refcount);
- gpuobj->refcount = 0;
- nouveau_gpuobj_del(dev, &gpuobj);
- }
-}
-int
-nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj)
+static void
+nouveau_gpuobj_del(struct kref *ref)
{
+ struct nouveau_gpuobj *gpuobj =
+ container_of(ref, struct nouveau_gpuobj, refcount);
+ struct drm_device *dev = gpuobj->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
- struct nouveau_gpuobj *gpuobj;
int i;
- NV_DEBUG(dev, "gpuobj %p\n", pgpuobj ? *pgpuobj : NULL);
-
- if (!dev_priv || !pgpuobj || !(*pgpuobj))
- return -EINVAL;
- gpuobj = *pgpuobj;
-
- if (gpuobj->refcount != 0) {
- NV_ERROR(dev, "gpuobj refcount is %d\n", gpuobj->refcount);
- return -EINVAL;
- }
+ NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
if (gpuobj->im_pramin && (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE)) {
- for (i = 0; i < gpuobj->im_pramin->size; i += 4)
- nv_wo32(dev, gpuobj, i/4, 0);
+ for (i = 0; i < gpuobj->size; i += 4)
+ nv_wo32(gpuobj, i, 0);
engine->instmem.flush(dev);
}
if (gpuobj->dtor)
gpuobj->dtor(dev, gpuobj);
- if (gpuobj->im_backing && !(gpuobj->flags & NVOBJ_FLAG_FAKE))
+ if (gpuobj->im_backing)
engine->instmem.clear(dev, gpuobj);
- if (gpuobj->im_pramin) {
- if (gpuobj->flags & NVOBJ_FLAG_FAKE)
- kfree(gpuobj->im_pramin);
- else
- drm_mm_put_block(gpuobj->im_pramin);
- }
-
+ spin_lock(&dev_priv->ramin_lock);
+ if (gpuobj->im_pramin)
+ drm_mm_put_block(gpuobj->im_pramin);
list_del(&gpuobj->list);
+ spin_unlock(&dev_priv->ramin_lock);
- *pgpuobj = NULL;
kfree(gpuobj);
- return 0;
}
-static int
-nouveau_gpuobj_instance_get(struct drm_device *dev,
- struct nouveau_channel *chan,
- struct nouveau_gpuobj *gpuobj, uint32_t *inst)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *cpramin;
-
- /* <NV50 use PRAMIN address everywhere */
- if (dev_priv->card_type < NV_50) {
- *inst = gpuobj->im_pramin->start;
- return 0;
- }
-
- if (chan && gpuobj->im_channel != chan) {
- NV_ERROR(dev, "Channel mismatch: obj %d, ref %d\n",
- gpuobj->im_channel->id, chan->id);
- return -EINVAL;
- }
-
- /* NV50 channel-local instance */
- if (chan) {
- cpramin = chan->ramin->gpuobj;
- *inst = gpuobj->im_pramin->start - cpramin->im_pramin->start;
- return 0;
- }
-
- /* NV50 global (VRAM) instance */
- if (!gpuobj->im_channel) {
- /* ...from global heap */
- if (!gpuobj->im_backing) {
- NV_ERROR(dev, "AII, no VRAM backing gpuobj\n");
- return -EINVAL;
- }
- *inst = gpuobj->im_backing_start;
- return 0;
- } else {
- /* ...from local heap */
- cpramin = gpuobj->im_channel->ramin->gpuobj;
- *inst = cpramin->im_backing_start +
- (gpuobj->im_pramin->start - cpramin->im_pramin->start);
- return 0;
- }
-
- return -EINVAL;
-}
-
-int
-nouveau_gpuobj_ref_add(struct drm_device *dev, struct nouveau_channel *chan,
- uint32_t handle, struct nouveau_gpuobj *gpuobj,
- struct nouveau_gpuobj_ref **ref_ret)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj_ref *ref;
- uint32_t instance;
- int ret;
-
- NV_DEBUG(dev, "ch%d h=0x%08x gpuobj=%p\n",
- chan ? chan->id : -1, handle, gpuobj);
-
- if (!dev_priv || !gpuobj || (ref_ret && *ref_ret != NULL))
- return -EINVAL;
-
- if (!chan && !ref_ret)
- return -EINVAL;
-
- if (gpuobj->engine == NVOBJ_ENGINE_SW && !gpuobj->im_pramin) {
- /* sw object */
- instance = 0x40;
- } else {
- ret = nouveau_gpuobj_instance_get(dev, chan, gpuobj, &instance);
- if (ret)
- return ret;
- }
-
- ref = kzalloc(sizeof(*ref), GFP_KERNEL);
- if (!ref)
- return -ENOMEM;
- INIT_LIST_HEAD(&ref->list);
- ref->gpuobj = gpuobj;
- ref->channel = chan;
- ref->instance = instance;
-
- if (!ref_ret) {
- ref->handle = handle;
-
- ret = nouveau_ramht_insert(dev, ref);
- if (ret) {
- kfree(ref);
- return ret;
- }
- } else {
- ref->handle = ~0;
- *ref_ret = ref;
- }
-
- ref->gpuobj->refcount++;
- return 0;
-}
-
-int nouveau_gpuobj_ref_del(struct drm_device *dev, struct nouveau_gpuobj_ref **pref)
-{
- struct nouveau_gpuobj_ref *ref;
-
- NV_DEBUG(dev, "ref %p\n", pref ? *pref : NULL);
-
- if (!dev || !pref || *pref == NULL)
- return -EINVAL;
- ref = *pref;
-
- if (ref->handle != ~0)
- nouveau_ramht_remove(dev, ref);
-
- if (ref->gpuobj) {
- ref->gpuobj->refcount--;
-
- if (ref->gpuobj->refcount == 0) {
- if (!(ref->gpuobj->flags & NVOBJ_FLAG_ALLOW_NO_REFS))
- nouveau_gpuobj_del(dev, &ref->gpuobj);
- }
- }
-
- *pref = NULL;
- kfree(ref);
- return 0;
-}
-
-int
-nouveau_gpuobj_new_ref(struct drm_device *dev,
- struct nouveau_channel *oc, struct nouveau_channel *rc,
- uint32_t handle, uint32_t size, int align,
- uint32_t flags, struct nouveau_gpuobj_ref **ref)
-{
- struct nouveau_gpuobj *gpuobj = NULL;
- int ret;
-
- ret = nouveau_gpuobj_new(dev, oc, size, align, flags, &gpuobj);
- if (ret)
- return ret;
-
- ret = nouveau_gpuobj_ref_add(dev, rc, handle, gpuobj, ref);
- if (ret) {
- nouveau_gpuobj_del(dev, &gpuobj);
- return ret;
- }
-
- return 0;
-}
-
-int
-nouveau_gpuobj_ref_find(struct nouveau_channel *chan, uint32_t handle,
- struct nouveau_gpuobj_ref **ref_ret)
+void
+nouveau_gpuobj_ref(struct nouveau_gpuobj *ref, struct nouveau_gpuobj **ptr)
{
- struct nouveau_gpuobj_ref *ref;
- struct list_head *entry, *tmp;
-
- list_for_each_safe(entry, tmp, &chan->ramht_refs) {
- ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
+ if (ref)
+ kref_get(&ref->refcount);
- if (ref->handle == handle) {
- if (ref_ret)
- *ref_ret = ref;
- return 0;
- }
- }
+ if (*ptr)
+ kref_put(&(*ptr)->refcount, nouveau_gpuobj_del);
- return -EINVAL;
+ *ptr = ref;
}
int
-nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset,
- uint32_t b_offset, uint32_t size,
- uint32_t flags, struct nouveau_gpuobj **pgpuobj,
- struct nouveau_gpuobj_ref **pref)
+nouveau_gpuobj_new_fake(struct drm_device *dev, u32 pinst, u64 vinst,
+ u32 size, u32 flags, struct nouveau_gpuobj **pgpuobj)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *gpuobj = NULL;
int i;
NV_DEBUG(dev,
- "p_offset=0x%08x b_offset=0x%08x size=0x%08x flags=0x%08x\n",
- p_offset, b_offset, size, flags);
+ "pinst=0x%08x vinst=0x%010llx size=0x%08x flags=0x%08x\n",
+ pinst, vinst, size, flags);
gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
if (!gpuobj)
return -ENOMEM;
NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
- gpuobj->im_channel = NULL;
- gpuobj->flags = flags | NVOBJ_FLAG_FAKE;
-
- list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
-
- if (p_offset != ~0) {
- gpuobj->im_pramin = kzalloc(sizeof(struct drm_mm_node),
- GFP_KERNEL);
- if (!gpuobj->im_pramin) {
- nouveau_gpuobj_del(dev, &gpuobj);
- return -ENOMEM;
- }
- gpuobj->im_pramin->start = p_offset;
- gpuobj->im_pramin->size = size;
- }
-
- if (b_offset != ~0) {
- gpuobj->im_backing = (struct nouveau_bo *)-1;
- gpuobj->im_backing_start = b_offset;
- }
+ gpuobj->dev = dev;
+ gpuobj->flags = flags;
+ kref_init(&gpuobj->refcount);
+ gpuobj->size = size;
+ gpuobj->pinst = pinst;
+ gpuobj->cinst = 0xdeadbeef;
+ gpuobj->vinst = vinst;
if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
- for (i = 0; i < gpuobj->im_pramin->size; i += 4)
- nv_wo32(dev, gpuobj, i/4, 0);
+ for (i = 0; i < gpuobj->size; i += 4)
+ nv_wo32(gpuobj, i, 0);
dev_priv->engine.instmem.flush(dev);
}
- if (pref) {
- i = nouveau_gpuobj_ref_add(dev, NULL, 0, gpuobj, pref);
- if (i) {
- nouveau_gpuobj_del(dev, &gpuobj);
- return i;
- }
- }
-
- if (pgpuobj)
- *pgpuobj = gpuobj;
+ spin_lock(&dev_priv->ramin_lock);
+ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+ spin_unlock(&dev_priv->ramin_lock);
+ *pgpuobj = gpuobj;
return 0;
}
@@ -685,14 +374,12 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class,
adjust = offset & 0x00000fff;
frame = offset & ~0x00000fff;
- nv_wo32(dev, *gpuobj, 0, ((1<<12) | (1<<13) |
- (adjust << 20) |
- (access << 14) |
- (target << 16) |
- class));
- nv_wo32(dev, *gpuobj, 1, size - 1);
- nv_wo32(dev, *gpuobj, 2, frame | pte_flags);
- nv_wo32(dev, *gpuobj, 3, frame | pte_flags);
+ nv_wo32(*gpuobj, 0, ((1<<12) | (1<<13) | (adjust << 20) |
+ (access << 14) | (target << 16) |
+ class));
+ nv_wo32(*gpuobj, 4, size - 1);
+ nv_wo32(*gpuobj, 8, frame | pte_flags);
+ nv_wo32(*gpuobj, 12, frame | pte_flags);
} else {
uint64_t limit = offset + size - 1;
uint32_t flags0, flags5;
@@ -705,12 +392,12 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class,
flags5 = 0x00080000;
}
- nv_wo32(dev, *gpuobj, 0, flags0 | class);
- nv_wo32(dev, *gpuobj, 1, lower_32_bits(limit));
- nv_wo32(dev, *gpuobj, 2, lower_32_bits(offset));
- nv_wo32(dev, *gpuobj, 3, ((upper_32_bits(limit) & 0xff) << 24) |
- (upper_32_bits(offset) & 0xff));
- nv_wo32(dev, *gpuobj, 5, flags5);
+ nv_wo32(*gpuobj, 0, flags0 | class);
+ nv_wo32(*gpuobj, 4, lower_32_bits(limit));
+ nv_wo32(*gpuobj, 8, lower_32_bits(offset));
+ nv_wo32(*gpuobj, 12, ((upper_32_bits(limit) & 0xff) << 24) |
+ (upper_32_bits(offset) & 0xff));
+ nv_wo32(*gpuobj, 20, flags5);
}
instmem->flush(dev);
@@ -741,7 +428,7 @@ nouveau_gpuobj_gart_dma_new(struct nouveau_channel *chan,
*o_ret = 0;
} else
if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) {
- *gpuobj = dev_priv->gart_info.sg_ctxdma;
+ nouveau_gpuobj_ref(dev_priv->gart_info.sg_ctxdma, gpuobj);
if (offset & ~0xffffffffULL) {
NV_ERROR(dev, "obj offset exceeds 32-bits\n");
return -EINVAL;
@@ -829,25 +516,25 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class,
}
if (dev_priv->card_type >= NV_50) {
- nv_wo32(dev, *gpuobj, 0, class);
- nv_wo32(dev, *gpuobj, 5, 0x00010000);
+ nv_wo32(*gpuobj, 0, class);
+ nv_wo32(*gpuobj, 20, 0x00010000);
} else {
switch (class) {
case NV_CLASS_NULL:
- nv_wo32(dev, *gpuobj, 0, 0x00001030);
- nv_wo32(dev, *gpuobj, 1, 0xFFFFFFFF);
+ nv_wo32(*gpuobj, 0, 0x00001030);
+ nv_wo32(*gpuobj, 4, 0xFFFFFFFF);
break;
default:
if (dev_priv->card_type >= NV_40) {
- nv_wo32(dev, *gpuobj, 0, class);
+ nv_wo32(*gpuobj, 0, class);
#ifdef __BIG_ENDIAN
- nv_wo32(dev, *gpuobj, 2, 0x01000000);
+ nv_wo32(*gpuobj, 8, 0x01000000);
#endif
} else {
#ifdef __BIG_ENDIAN
- nv_wo32(dev, *gpuobj, 0, class | 0x00080000);
+ nv_wo32(*gpuobj, 0, class | 0x00080000);
#else
- nv_wo32(dev, *gpuobj, 0, class);
+ nv_wo32(*gpuobj, 0, class);
#endif
}
}
@@ -873,10 +560,15 @@ nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class,
gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
if (!gpuobj)
return -ENOMEM;
+ gpuobj->dev = chan->dev;
gpuobj->engine = NVOBJ_ENGINE_SW;
gpuobj->class = class;
+ kref_init(&gpuobj->refcount);
+ gpuobj->cinst = 0x40;
+ spin_lock(&dev_priv->ramin_lock);
list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+ spin_unlock(&dev_priv->ramin_lock);
*gpuobj_ret = gpuobj;
return 0;
}
@@ -886,7 +578,6 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *pramin = NULL;
uint32_t size;
uint32_t base;
int ret;
@@ -911,18 +602,16 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
size += 0x1000;
}
- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, size, 0x1000, 0,
- &chan->ramin);
+ ret = nouveau_gpuobj_new(dev, NULL, size, 0x1000, 0, &chan->ramin);
if (ret) {
NV_ERROR(dev, "Error allocating channel PRAMIN: %d\n", ret);
return ret;
}
- pramin = chan->ramin->gpuobj;
- ret = drm_mm_init(&chan->ramin_heap, pramin->im_pramin->start + base, size);
+ ret = drm_mm_init(&chan->ramin_heap, base, size);
if (ret) {
NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret);
- nouveau_gpuobj_ref_del(dev, &chan->ramin);
+ nouveau_gpuobj_ref(NULL, &chan->ramin);
return ret;
}
@@ -939,8 +628,6 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
struct nouveau_gpuobj *vram = NULL, *tt = NULL;
int ret, i;
- INIT_LIST_HEAD(&chan->ramht_refs);
-
NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
/* Allocate a chunk of memory for per-channel object storage */
@@ -956,41 +643,38 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
* locations determined during init.
*/
if (dev_priv->card_type >= NV_50) {
- uint32_t vm_offset, pde;
+ u32 pgd_offs = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200;
+ u64 vm_vinst = chan->ramin->vinst + pgd_offs;
+ u32 vm_pinst = chan->ramin->pinst;
+ u32 pde;
- vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200;
- vm_offset += chan->ramin->gpuobj->im_pramin->start;
+ if (vm_pinst != ~0)
+ vm_pinst += pgd_offs;
- ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000,
- 0, &chan->vm_pd, NULL);
+ ret = nouveau_gpuobj_new_fake(dev, vm_pinst, vm_vinst, 0x4000,
+ 0, &chan->vm_pd);
if (ret)
return ret;
for (i = 0; i < 0x4000; i += 8) {
- nv_wo32(dev, chan->vm_pd, (i+0)/4, 0x00000000);
- nv_wo32(dev, chan->vm_pd, (i+4)/4, 0xdeadcafe);
+ nv_wo32(chan->vm_pd, i + 0, 0x00000000);
+ nv_wo32(chan->vm_pd, i + 4, 0xdeadcafe);
}
- pde = (dev_priv->vm_gart_base / (512*1024*1024)) * 2;
- ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
- dev_priv->gart_info.sg_ctxdma,
- &chan->vm_gart_pt);
- if (ret)
- return ret;
- nv_wo32(dev, chan->vm_pd, pde++,
- chan->vm_gart_pt->instance | 0x03);
- nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
+ nouveau_gpuobj_ref(dev_priv->gart_info.sg_ctxdma,
+ &chan->vm_gart_pt);
+ pde = (dev_priv->vm_gart_base / (512*1024*1024)) * 8;
+ nv_wo32(chan->vm_pd, pde + 0, chan->vm_gart_pt->vinst | 3);
+ nv_wo32(chan->vm_pd, pde + 4, 0x00000000);
- pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 2;
+ pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 8;
for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
- ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
- dev_priv->vm_vram_pt[i],
- &chan->vm_vram_pt[i]);
- if (ret)
- return ret;
+ nouveau_gpuobj_ref(dev_priv->vm_vram_pt[i],
+ &chan->vm_vram_pt[i]);
- nv_wo32(dev, chan->vm_pd, pde++,
- chan->vm_vram_pt[i]->instance | 0x61);
- nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
+ nv_wo32(chan->vm_pd, pde + 0,
+ chan->vm_vram_pt[i]->vinst | 0x61);
+ nv_wo32(chan->vm_pd, pde + 4, 0x00000000);
+ pde += 8;
}
instmem->flush(dev);
@@ -998,15 +682,17 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
/* RAMHT */
if (dev_priv->card_type < NV_50) {
- ret = nouveau_gpuobj_ref_add(dev, NULL, 0, dev_priv->ramht,
- &chan->ramht);
+ nouveau_ramht_ref(dev_priv->ramht, &chan->ramht, NULL);
+ } else {
+ struct nouveau_gpuobj *ramht = NULL;
+
+ ret = nouveau_gpuobj_new(dev, chan, 0x8000, 16,
+ NVOBJ_FLAG_ZERO_ALLOC, &ramht);
if (ret)
return ret;
- } else {
- ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0,
- 0x8000, 16,
- NVOBJ_FLAG_ZERO_ALLOC,
- &chan->ramht);
+
+ ret = nouveau_ramht_new(dev, ramht, &chan->ramht);
+ nouveau_gpuobj_ref(NULL, &ramht);
if (ret)
return ret;
}
@@ -1023,24 +709,32 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
}
} else {
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
- 0, dev_priv->fb_available_size,
- NV_DMA_ACCESS_RW,
- NV_DMA_TARGET_VIDMEM, &vram);
+ 0, dev_priv->fb_available_size,
+ NV_DMA_ACCESS_RW,
+ NV_DMA_TARGET_VIDMEM, &vram);
if (ret) {
NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
return ret;
}
}
- ret = nouveau_gpuobj_ref_add(dev, chan, vram_h, vram, NULL);
+ ret = nouveau_ramht_insert(chan, vram_h, vram);
+ nouveau_gpuobj_ref(NULL, &vram);
if (ret) {
- NV_ERROR(dev, "Error referencing VRAM ctxdma: %d\n", ret);
+ NV_ERROR(dev, "Error adding VRAM ctxdma to RAMHT: %d\n", ret);
return ret;
}
/* TT memory ctxdma */
if (dev_priv->card_type >= NV_50) {
- tt = vram;
+ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
+ 0, dev_priv->vm_end,
+ NV_DMA_ACCESS_RW,
+ NV_DMA_TARGET_AGP, &tt);
+ if (ret) {
+ NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
+ return ret;
+ }
} else
if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) {
ret = nouveau_gpuobj_gart_dma_new(chan, 0,
@@ -1056,9 +750,10 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
return ret;
}
- ret = nouveau_gpuobj_ref_add(dev, chan, tt_h, tt, NULL);
+ ret = nouveau_ramht_insert(chan, tt_h, tt);
+ nouveau_gpuobj_ref(NULL, &tt);
if (ret) {
- NV_ERROR(dev, "Error referencing TT ctxdma: %d\n", ret);
+ NV_ERROR(dev, "Error adding TT ctxdma to RAMHT: %d\n", ret);
return ret;
}
@@ -1070,33 +765,23 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct drm_device *dev = chan->dev;
- struct list_head *entry, *tmp;
- struct nouveau_gpuobj_ref *ref;
int i;
NV_DEBUG(dev, "ch%d\n", chan->id);
- if (!chan->ramht_refs.next)
+ if (!chan->ramht)
return;
- list_for_each_safe(entry, tmp, &chan->ramht_refs) {
- ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
-
- nouveau_gpuobj_ref_del(dev, &ref);
- }
-
- nouveau_gpuobj_ref_del(dev, &chan->ramht);
+ nouveau_ramht_ref(NULL, &chan->ramht, chan);
- nouveau_gpuobj_del(dev, &chan->vm_pd);
- nouveau_gpuobj_ref_del(dev, &chan->vm_gart_pt);
+ nouveau_gpuobj_ref(NULL, &chan->vm_pd);
+ nouveau_gpuobj_ref(NULL, &chan->vm_gart_pt);
for (i = 0; i < dev_priv->vm_vram_pt_nr; i++)
- nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
+ nouveau_gpuobj_ref(NULL, &chan->vm_vram_pt[i]);
if (chan->ramin_heap.free_stack.next)
drm_mm_takedown(&chan->ramin_heap);
- if (chan->ramin)
- nouveau_gpuobj_ref_del(dev, &chan->ramin);
-
+ nouveau_gpuobj_ref(NULL, &chan->ramin);
}
int
@@ -1117,17 +802,17 @@ nouveau_gpuobj_suspend(struct drm_device *dev)
}
list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
- if (!gpuobj->im_backing || (gpuobj->flags & NVOBJ_FLAG_FAKE))
+ if (!gpuobj->im_backing)
continue;
- gpuobj->im_backing_suspend = vmalloc(gpuobj->im_pramin->size);
+ gpuobj->im_backing_suspend = vmalloc(gpuobj->size);
if (!gpuobj->im_backing_suspend) {
nouveau_gpuobj_resume(dev);
return -ENOMEM;
}
- for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
- gpuobj->im_backing_suspend[i] = nv_ro32(dev, gpuobj, i);
+ for (i = 0; i < gpuobj->size; i += 4)
+ gpuobj->im_backing_suspend[i/4] = nv_ro32(gpuobj, i);
}
return 0;
@@ -1172,8 +857,8 @@ nouveau_gpuobj_resume(struct drm_device *dev)
if (!gpuobj->im_backing_suspend)
continue;
- for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
- nv_wo32(dev, gpuobj, i, gpuobj->im_backing_suspend[i]);
+ for (i = 0; i < gpuobj->size; i += 4)
+ nv_wo32(gpuobj, i, gpuobj->im_backing_suspend[i/4]);
dev_priv->engine.instmem.flush(dev);
}
@@ -1208,25 +893,24 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data,
return -EPERM;
}
- if (nouveau_gpuobj_ref_find(chan, init->handle, NULL) == 0)
+ if (nouveau_ramht_find(chan, init->handle))
return -EEXIST;
if (!grc->software)
ret = nouveau_gpuobj_gr_new(chan, grc->id, &gr);
else
ret = nouveau_gpuobj_sw_new(chan, grc->id, &gr);
-
if (ret) {
NV_ERROR(dev, "Error creating object: %d (%d/0x%08x)\n",
ret, init->channel, init->handle);
return ret;
}
- ret = nouveau_gpuobj_ref_add(dev, chan, init->handle, gr, NULL);
+ ret = nouveau_ramht_insert(chan, init->handle, gr);
+ nouveau_gpuobj_ref(NULL, &gr);
if (ret) {
NV_ERROR(dev, "Error referencing object: %d (%d/0x%08x)\n",
ret, init->channel, init->handle);
- nouveau_gpuobj_del(dev, &gr);
return ret;
}
@@ -1237,16 +921,62 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_gpuobj_free *objfree = data;
- struct nouveau_gpuobj_ref *ref;
+ struct nouveau_gpuobj *gpuobj;
struct nouveau_channel *chan;
- int ret;
NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(objfree->channel, file_priv, chan);
- ret = nouveau_gpuobj_ref_find(chan, objfree->handle, &ref);
- if (ret)
- return ret;
- nouveau_gpuobj_ref_del(dev, &ref);
+ gpuobj = nouveau_ramht_find(chan, objfree->handle);
+ if (!gpuobj)
+ return -ENOENT;
+ nouveau_ramht_remove(chan, objfree->handle);
return 0;
}
+
+u32
+nv_ro32(struct nouveau_gpuobj *gpuobj, u32 offset)
+{
+ struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
+ struct drm_device *dev = gpuobj->dev;
+
+ if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) {
+ u64 ptr = gpuobj->vinst + offset;
+ u32 base = ptr >> 16;
+ u32 val;
+
+ spin_lock(&dev_priv->ramin_lock);
+ if (dev_priv->ramin_base != base) {
+ dev_priv->ramin_base = base;
+ nv_wr32(dev, 0x001700, dev_priv->ramin_base);
+ }
+ val = nv_rd32(dev, 0x700000 + (ptr & 0xffff));
+ spin_unlock(&dev_priv->ramin_lock);
+ return val;
+ }
+
+ return nv_ri32(dev, gpuobj->pinst + offset);
+}
+
+void
+nv_wo32(struct nouveau_gpuobj *gpuobj, u32 offset, u32 val)
+{
+ struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
+ struct drm_device *dev = gpuobj->dev;
+
+ if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) {
+ u64 ptr = gpuobj->vinst + offset;
+ u32 base = ptr >> 16;
+
+ spin_lock(&dev_priv->ramin_lock);
+ if (dev_priv->ramin_base != base) {
+ dev_priv->ramin_base = base;
+ nv_wr32(dev, 0x001700, dev_priv->ramin_base);
+ }
+ nv_wr32(dev, 0x700000 + (ptr & 0xffff), val);
+ spin_unlock(&dev_priv->ramin_lock);
+ return;
+ }
+
+ nv_wi32(dev, gpuobj->pinst + offset, val);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c
new file mode 100644
index 000000000000..ac62a1b8c4fc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_pm.h"
+
+static void
+legacy_perf_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ char *perf, *entry, *bmp = &bios->data[bios->offset];
+ int headerlen, use_straps;
+
+ if (bmp[5] < 0x5 || bmp[6] < 0x14) {
+ NV_DEBUG(dev, "BMP version too old for perf\n");
+ return;
+ }
+
+ perf = ROMPTR(bios, bmp[0x73]);
+ if (!perf) {
+ NV_DEBUG(dev, "No memclock table pointer found.\n");
+ return;
+ }
+
+ switch (perf[0]) {
+ case 0x12:
+ case 0x14:
+ case 0x18:
+ use_straps = 0;
+ headerlen = 1;
+ break;
+ case 0x01:
+ use_straps = perf[1] & 1;
+ headerlen = (use_straps ? 8 : 2);
+ break;
+ default:
+ NV_WARN(dev, "Unknown memclock table version %x.\n", perf[0]);
+ return;
+ }
+
+ entry = perf + headerlen;
+ if (use_straps)
+ entry += (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x3c) >> 1;
+
+ sprintf(pm->perflvl[0].name, "performance_level_0");
+ pm->perflvl[0].memory = ROM16(entry[0]) * 20;
+ pm->nr_perflvl = 1;
+}
+
+void
+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;
+ 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(bios, P.data[0]);
+ version = perf[0];
+ headerlen = perf[1];
+ if (version < 0x40) {
+ recordlen = perf[3] + (perf[4] * perf[5]);
+ entries = perf[2];
+ } else {
+ recordlen = perf[2] + (perf[3] * perf[4]);
+ entries = perf[5];
+ }
+ } else {
+ if (bios->data[bios->offset + 6] < 0x25) {
+ legacy_perf_init(dev);
+ return;
+ }
+
+ perf = ROMPTR(bios, 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];
+ }
+
+ entry = perf + headerlen;
+ for (i = 0; i < entries; i++) {
+ struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
+
+ if (entry[0] == 0xff) {
+ entry += recordlen;
+ continue;
+ }
+
+ switch (version) {
+ case 0x12:
+ case 0x13:
+ case 0x15:
+ perflvl->fanspeed = entry[55];
+ perflvl->voltage = entry[56];
+ perflvl->core = ROM32(entry[1]) * 10;
+ perflvl->memory = ROM32(entry[5]) * 20;
+ break;
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ perflvl->fanspeed = entry[4];
+ perflvl->voltage = entry[5];
+ perflvl->core = ROM16(entry[6]) * 1000;
+
+ if (dev_priv->chipset == 0x49 ||
+ dev_priv->chipset == 0x4b)
+ perflvl->memory = ROM16(entry[11]) * 1000;
+ else
+ perflvl->memory = ROM16(entry[11]) * 2000;
+
+ break;
+ case 0x25:
+ perflvl->fanspeed = entry[4];
+ perflvl->voltage = entry[5];
+ perflvl->core = ROM16(entry[6]) * 1000;
+ perflvl->shader = ROM16(entry[10]) * 1000;
+ perflvl->memory = ROM16(entry[12]) * 1000;
+ break;
+ case 0x30:
+ perflvl->memscript = ROM16(entry[2]);
+ case 0x35:
+ perflvl->fanspeed = entry[6];
+ perflvl->voltage = entry[7];
+ perflvl->core = ROM16(entry[8]) * 1000;
+ perflvl->shader = ROM16(entry[10]) * 1000;
+ perflvl->memory = ROM16(entry[12]) * 1000;
+ /*XXX: confirm on 0x35 */
+ perflvl->unk05 = ROM16(entry[16]) * 1000;
+ break;
+ case 0x40:
+#define subent(n) entry[perf[2] + ((n) * perf[3])]
+ perflvl->fanspeed = 0; /*XXX*/
+ perflvl->voltage = entry[2];
+ perflvl->core = (ROM16(subent(0)) & 0xfff) * 1000;
+ perflvl->shader = (ROM16(subent(1)) & 0xfff) * 1000;
+ perflvl->memory = (ROM16(subent(2)) & 0xfff) * 1000;
+ break;
+ }
+
+ /* make sure vid is valid */
+ if (pm->voltage.supported && perflvl->voltage) {
+ vid = nouveau_volt_vid_lookup(dev, perflvl->voltage);
+ if (vid < 0) {
+ NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
+ entry += recordlen;
+ continue;
+ }
+ }
+
+ snprintf(perflvl->name, sizeof(perflvl->name),
+ "performance_level_%d", i);
+ perflvl->id = i;
+ pm->nr_perflvl++;
+
+ entry += recordlen;
+ }
+}
+
+void
+nouveau_perf_fini(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
new file mode 100644
index 000000000000..1c99c55d6d46
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_pm.h"
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+static int
+nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl,
+ u8 id, u32 khz)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ void *pre_state;
+
+ if (khz == 0)
+ return 0;
+
+ pre_state = pm->clock_pre(dev, perflvl, id, khz);
+ if (IS_ERR(pre_state))
+ return PTR_ERR(pre_state);
+
+ if (pre_state)
+ pm->clock_set(dev, pre_state);
+ return 0;
+}
+
+static int
+nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ int ret;
+
+ if (perflvl == pm->cur)
+ return 0;
+
+ if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {
+ ret = pm->voltage_set(dev, perflvl->voltage);
+ if (ret) {
+ NV_ERROR(dev, "voltage_set %d failed: %d\n",
+ perflvl->voltage, ret);
+ }
+ }
+
+ nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
+ nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
+ nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
+ nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
+
+ pm->cur = perflvl;
+ return 0;
+}
+
+static int
+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;
+
+ /* safety precaution, for now */
+ if (nouveau_perflvl_wr != 7777)
+ return -EPERM;
+
+ if (!pm->clock_set)
+ return -EINVAL;
+
+ if (!strncmp(profile, "boot", 4))
+ perflvl = &pm->boot;
+ else {
+ int pl = simple_strtol(profile, NULL, 10);
+ int i;
+
+ for (i = 0; i < pm->nr_perflvl; i++) {
+ if (pm->perflvl[i].id == pl) {
+ perflvl = &pm->perflvl[i];
+ break;
+ }
+ }
+
+ if (!perflvl)
+ return -EINVAL;
+ }
+
+ NV_INFO(dev, "setting performance level: %s\n", profile);
+ return nouveau_pm_perflvl_set(dev, perflvl);
+}
+
+static int
+nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ int ret;
+
+ if (!pm->clock_get)
+ return -EINVAL;
+
+ memset(perflvl, 0, sizeof(*perflvl));
+
+ ret = pm->clock_get(dev, PLL_CORE);
+ if (ret > 0)
+ perflvl->core = ret;
+
+ ret = pm->clock_get(dev, PLL_MEMORY);
+ if (ret > 0)
+ perflvl->memory = ret;
+
+ ret = pm->clock_get(dev, PLL_SHADER);
+ if (ret > 0)
+ perflvl->shader = ret;
+
+ ret = pm->clock_get(dev, PLL_UNK05);
+ if (ret > 0)
+ perflvl->unk05 = ret;
+
+ if (pm->voltage.supported && pm->voltage_get) {
+ ret = pm->voltage_get(dev);
+ if (ret > 0)
+ perflvl->voltage = ret;
+ }
+
+ return 0;
+}
+
+static void
+nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
+{
+ char c[16], s[16], v[16], f[16];
+
+ c[0] = '\0';
+ if (perflvl->core)
+ snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
+
+ s[0] = '\0';
+ if (perflvl->shader)
+ snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
+
+ v[0] = '\0';
+ if (perflvl->voltage)
+ snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10);
+
+ f[0] = '\0';
+ if (perflvl->fanspeed)
+ snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
+
+ snprintf(ptr, len, "memory %dMHz%s%s%s%s\n", perflvl->memory / 1000,
+ c, s, 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;
+ char *ptr = buf;
+ int len = PAGE_SIZE;
+
+ snprintf(ptr, len, "%d: ", perflvl->id);
+ ptr += strlen(buf);
+ len -= strlen(buf);
+
+ nouveau_pm_perflvl_info(perflvl, ptr, len);
+ return strlen(buf);
+}
+
+static ssize_t
+nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
+{
+ struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_level cur;
+ 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);
+ ptr += strlen(buf);
+ len -= strlen(buf);
+
+ ret = nouveau_pm_perflvl_get(dev, &cur);
+ if (ret == 0)
+ nouveau_pm_perflvl_info(&cur, ptr, len);
+ return strlen(buf);
+}
+
+static ssize_t
+nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
+ const char *buf, size_t count)
+{
+ struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
+ int ret;
+
+ ret = nouveau_pm_profile_set(dev, buf);
+ if (ret)
+ return ret;
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
+ nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
+
+static int
+nouveau_sysfs_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct device *d = &dev->pdev->dev;
+ int ret, i;
+
+ ret = device_create_file(d, &dev_attr_performance_level);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pm->nr_perflvl; i++) {
+ struct nouveau_pm_level *perflvl = &pm->perflvl[i];
+
+ perflvl->dev_attr.attr.name = perflvl->name;
+ perflvl->dev_attr.attr.mode = S_IRUGO;
+ perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
+ perflvl->dev_attr.store = NULL;
+ sysfs_attr_init(&perflvl->dev_attr.attr);
+
+ ret = device_create_file(d, &perflvl->dev_attr);
+ if (ret) {
+ NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
+ perflvl->id, i);
+ perflvl->dev_attr.attr.name = NULL;
+ nouveau_pm_fini(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void
+nouveau_sysfs_fini(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct device *d = &dev->pdev->dev;
+ int i;
+
+ device_remove_file(d, &dev_attr_performance_level);
+ for (i = 0; i < pm->nr_perflvl; i++) {
+ struct nouveau_pm_level *pl = &pm->perflvl[i];
+
+ if (!pl->dev_attr.attr.name)
+ break;
+
+ device_remove_file(d, &pl->dev_attr);
+ }
+}
+
+static ssize_t
+nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
+{
+ struct drm_device *dev = dev_get_drvdata(d);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
+}
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
+ NULL, 0);
+
+static ssize_t
+nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
+{
+ struct drm_device *dev = dev_get_drvdata(d);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
+}
+static ssize_t
+nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
+ const char *buf, size_t count)
+{
+ struct drm_device *dev = dev_get_drvdata(d);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
+ long value;
+
+ if (strict_strtol(buf, 10, &value) == -EINVAL)
+ return count;
+
+ temp->down_clock = value/1000;
+
+ nouveau_temp_safety_checks(dev);
+
+ return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
+ nouveau_hwmon_set_max_temp,
+ 0);
+
+static ssize_t
+nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
+ char *buf)
+{
+ struct drm_device *dev = dev_get_drvdata(d);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
+}
+static ssize_t
+nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
+ const char *buf,
+ size_t count)
+{
+ struct drm_device *dev = dev_get_drvdata(d);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
+ long value;
+
+ if (strict_strtol(buf, 10, &value) == -EINVAL)
+ return count;
+
+ temp->critical = value/1000;
+
+ nouveau_temp_safety_checks(dev);
+
+ return count;
+}
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
+ nouveau_hwmon_critical_temp,
+ nouveau_hwmon_set_critical_temp,
+ 0);
+
+static ssize_t nouveau_hwmon_show_name(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "nouveau\n");
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
+
+static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "1000\n");
+}
+static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
+ nouveau_hwmon_show_update_rate,
+ NULL, 0);
+
+static struct attribute *hwmon_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_name.dev_attr.attr,
+ &sensor_dev_attr_update_rate.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group hwmon_attrgroup = {
+ .attrs = hwmon_attributes,
+};
+
+static int
+nouveau_hwmon_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct device *hwmon_dev;
+ int ret;
+
+ if (!pm->temp_get)
+ return -ENODEV;
+
+ hwmon_dev = hwmon_device_register(&dev->pdev->dev);
+ if (IS_ERR(hwmon_dev)) {
+ ret = PTR_ERR(hwmon_dev);
+ NV_ERROR(dev,
+ "Unable to register hwmon device: %d\n", ret);
+ return ret;
+ }
+ dev_set_drvdata(hwmon_dev, dev);
+ ret = sysfs_create_group(&hwmon_dev->kobj,
+ &hwmon_attrgroup);
+ if (ret) {
+ NV_ERROR(dev,
+ "Unable to create hwmon sysfs file: %d\n", ret);
+ hwmon_device_unregister(hwmon_dev);
+ return ret;
+ }
+
+ pm->hwmon = hwmon_dev;
+
+ return 0;
+}
+
+static void
+nouveau_hwmon_fini(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+
+ if (pm->hwmon) {
+ sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup);
+ hwmon_device_unregister(pm->hwmon);
+ }
+}
+
+int
+nouveau_pm_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ char info[256];
+ int ret, i;
+
+ nouveau_volt_init(dev);
+ nouveau_perf_init(dev);
+ nouveau_temp_init(dev);
+ nouveau_mem_timing_init(dev);
+
+ 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) {
+ pm->cur = &pm->boot;
+
+ 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);
+ }
+ }
+
+ nouveau_sysfs_init(dev);
+ nouveau_hwmon_init(dev);
+
+ return 0;
+}
+
+void
+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;
+
+ if (pm->cur != &pm->boot)
+ nouveau_pm_perflvl_set(dev, &pm->boot);
+
+ nouveau_mem_timing_fini(dev);
+ nouveau_temp_fini(dev);
+ nouveau_perf_fini(dev);
+ nouveau_volt_fini(dev);
+
+ nouveau_hwmon_fini(dev);
+ nouveau_sysfs_fini(dev);
+}
+
+void
+nouveau_pm_resume(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_level *perflvl;
+
+ if (pm->cur == &pm->boot)
+ return;
+
+ perflvl = pm->cur;
+ pm->cur = &pm->boot;
+ nouveau_pm_perflvl_set(dev, perflvl);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h
new file mode 100644
index 000000000000..4a9838ddacec
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifndef __NOUVEAU_PM_H__
+#define __NOUVEAU_PM_H__
+
+/* 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);
+
+/* nouveau_volt.c */
+void nouveau_volt_init(struct drm_device *);
+void nouveau_volt_fini(struct drm_device *);
+int nouveau_volt_vid_lookup(struct drm_device *, int voltage);
+int nouveau_volt_lvl_lookup(struct drm_device *, int vid);
+int nouveau_voltage_gpio_get(struct drm_device *);
+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 *);
+
+/* nouveau_mem.c */
+void nouveau_mem_timing_init(struct drm_device *);
+void nouveau_mem_timing_fini(struct drm_device *);
+
+/* nv04_pm.c */
+int nv04_pm_clock_get(struct drm_device *, u32 id);
+void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
+ u32 id, int khz);
+void nv04_pm_clock_set(struct drm_device *, void *);
+
+/* nv50_pm.c */
+int nv50_pm_clock_get(struct drm_device *, u32 id);
+void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
+ u32 id, int khz);
+void nv50_pm_clock_set(struct drm_device *, void *);
+
+/* nva3_pm.c */
+int nva3_pm_clock_get(struct drm_device *, u32 id);
+void *nva3_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
+ u32 id, int khz);
+void nva3_pm_clock_set(struct drm_device *, void *);
+
+/* nouveau_temp.c */
+void nouveau_temp_init(struct drm_device *dev);
+void nouveau_temp_fini(struct drm_device *dev);
+void nouveau_temp_safety_checks(struct drm_device *dev);
+int nv40_temp_get(struct drm_device *dev);
+int nv84_temp_get(struct drm_device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_ramht.c b/drivers/gpu/drm/nouveau/nouveau_ramht.c
new file mode 100644
index 000000000000..7f16697cc96c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_ramht.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
+
+static u32
+nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_ramht *ramht = chan->ramht;
+ u32 hash = 0;
+ int i;
+
+ NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle);
+
+ for (i = 32; i > 0; i -= ramht->bits) {
+ hash ^= (handle & ((1 << ramht->bits) - 1));
+ handle >>= ramht->bits;
+ }
+
+ if (dev_priv->card_type < NV_50)
+ hash ^= chan->id << (ramht->bits - 4);
+ hash <<= 3;
+
+ NV_DEBUG(dev, "hash=0x%08x\n", hash);
+ return hash;
+}
+
+static int
+nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
+ u32 offset)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 ctx = nv_ro32(ramht, offset + 4);
+
+ if (dev_priv->card_type < NV_40)
+ return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
+ return (ctx != 0);
+}
+
+static int
+nouveau_ramht_entry_same_channel(struct nouveau_channel *chan,
+ struct nouveau_gpuobj *ramht, u32 offset)
+{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+ u32 ctx = nv_ro32(ramht, offset + 4);
+
+ if (dev_priv->card_type >= NV_50)
+ return true;
+ else if (dev_priv->card_type >= NV_40)
+ return chan->id ==
+ ((ctx >> NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);
+ else
+ return chan->id ==
+ ((ctx >> NV_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);
+}
+
+int
+nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
+ struct nouveau_gpuobj *gpuobj)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+ struct nouveau_ramht_entry *entry;
+ struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
+ unsigned long flags;
+ u32 ctx, co, ho;
+
+ if (nouveau_ramht_find(chan, handle))
+ return -EEXIST;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+ entry->channel = chan;
+ entry->gpuobj = NULL;
+ entry->handle = handle;
+ nouveau_gpuobj_ref(gpuobj, &entry->gpuobj);
+
+ if (dev_priv->card_type < NV_40) {
+ ctx = NV_RAMHT_CONTEXT_VALID | (gpuobj->cinst >> 4) |
+ (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
+ (gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
+ } else
+ if (dev_priv->card_type < NV_50) {
+ ctx = (gpuobj->cinst >> 4) |
+ (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
+ (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
+ } else {
+ if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
+ ctx = (gpuobj->cinst << 10) | 2;
+ } else {
+ ctx = (gpuobj->cinst >> 4) |
+ ((gpuobj->engine <<
+ NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
+ }
+ }
+
+ spin_lock_irqsave(&chan->ramht->lock, flags);
+ list_add(&entry->head, &chan->ramht->entries);
+
+ co = ho = nouveau_ramht_hash_handle(chan, handle);
+ do {
+ if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
+ NV_DEBUG(dev,
+ "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
+ chan->id, co, handle, ctx);
+ nv_wo32(ramht, co + 0, handle);
+ nv_wo32(ramht, co + 4, ctx);
+
+ spin_unlock_irqrestore(&chan->ramht->lock, flags);
+ instmem->flush(dev);
+ return 0;
+ }
+ NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
+ chan->id, co, nv_ro32(ramht, co));
+
+ co += 8;
+ if (co >= ramht->size)
+ co = 0;
+ } while (co != ho);
+
+ NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
+ list_del(&entry->head);
+ spin_unlock_irqrestore(&chan->ramht->lock, flags);
+ kfree(entry);
+ return -ENOMEM;
+}
+
+static void
+nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+ struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
+ struct nouveau_ramht_entry *entry, *tmp;
+ u32 co, ho;
+
+ list_for_each_entry_safe(entry, tmp, &chan->ramht->entries, head) {
+ if (entry->channel != chan || entry->handle != handle)
+ continue;
+
+ nouveau_gpuobj_ref(NULL, &entry->gpuobj);
+ list_del(&entry->head);
+ kfree(entry);
+ break;
+ }
+
+ co = ho = nouveau_ramht_hash_handle(chan, handle);
+ do {
+ if (nouveau_ramht_entry_valid(dev, ramht, co) &&
+ nouveau_ramht_entry_same_channel(chan, ramht, co) &&
+ (handle == nv_ro32(ramht, co))) {
+ NV_DEBUG(dev,
+ "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
+ chan->id, co, handle, nv_ro32(ramht, co + 4));
+ nv_wo32(ramht, co + 0, 0x00000000);
+ nv_wo32(ramht, co + 4, 0x00000000);
+ instmem->flush(dev);
+ return;
+ }
+
+ co += 8;
+ if (co >= ramht->size)
+ co = 0;
+ } while (co != ho);
+
+ NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
+ chan->id, handle);
+}
+
+void
+nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
+{
+ struct nouveau_ramht *ramht = chan->ramht;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ramht->lock, flags);
+ nouveau_ramht_remove_locked(chan, handle);
+ spin_unlock_irqrestore(&ramht->lock, flags);
+}
+
+struct nouveau_gpuobj *
+nouveau_ramht_find(struct nouveau_channel *chan, u32 handle)
+{
+ struct nouveau_ramht *ramht = chan->ramht;
+ struct nouveau_ramht_entry *entry;
+ struct nouveau_gpuobj *gpuobj = NULL;
+ unsigned long flags;
+
+ if (unlikely(!chan->ramht))
+ return NULL;
+
+ spin_lock_irqsave(&ramht->lock, flags);
+ list_for_each_entry(entry, &chan->ramht->entries, head) {
+ if (entry->channel == chan && entry->handle == handle) {
+ gpuobj = entry->gpuobj;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ramht->lock, flags);
+
+ return gpuobj;
+}
+
+int
+nouveau_ramht_new(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
+ struct nouveau_ramht **pramht)
+{
+ struct nouveau_ramht *ramht;
+
+ ramht = kzalloc(sizeof(*ramht), GFP_KERNEL);
+ if (!ramht)
+ return -ENOMEM;
+
+ ramht->dev = dev;
+ kref_init(&ramht->refcount);
+ ramht->bits = drm_order(gpuobj->size / 8);
+ INIT_LIST_HEAD(&ramht->entries);
+ spin_lock_init(&ramht->lock);
+ nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj);
+
+ *pramht = ramht;
+ return 0;
+}
+
+static void
+nouveau_ramht_del(struct kref *ref)
+{
+ struct nouveau_ramht *ramht =
+ container_of(ref, struct nouveau_ramht, refcount);
+
+ nouveau_gpuobj_ref(NULL, &ramht->gpuobj);
+ kfree(ramht);
+}
+
+void
+nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr,
+ struct nouveau_channel *chan)
+{
+ struct nouveau_ramht_entry *entry, *tmp;
+ struct nouveau_ramht *ramht;
+ unsigned long flags;
+
+ if (ref)
+ kref_get(&ref->refcount);
+
+ ramht = *ptr;
+ if (ramht) {
+ spin_lock_irqsave(&ramht->lock, flags);
+ list_for_each_entry_safe(entry, tmp, &ramht->entries, head) {
+ if (entry->channel != chan)
+ continue;
+
+ nouveau_ramht_remove_locked(chan, entry->handle);
+ }
+ spin_unlock_irqrestore(&ramht->lock, flags);
+
+ kref_put(&ramht->refcount, nouveau_ramht_del);
+ }
+ *ptr = ref;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_ramht.h b/drivers/gpu/drm/nouveau/nouveau_ramht.h
new file mode 100644
index 000000000000..b79cb5e1a8f1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_ramht.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifndef __NOUVEAU_RAMHT_H__
+#define __NOUVEAU_RAMHT_H__
+
+struct nouveau_ramht_entry {
+ struct list_head head;
+ struct nouveau_channel *channel;
+ struct nouveau_gpuobj *gpuobj;
+ u32 handle;
+};
+
+struct nouveau_ramht {
+ struct drm_device *dev;
+ struct kref refcount;
+ spinlock_t lock;
+ struct nouveau_gpuobj *gpuobj;
+ struct list_head entries;
+ int bits;
+};
+
+extern int nouveau_ramht_new(struct drm_device *, struct nouveau_gpuobj *,
+ struct nouveau_ramht **);
+extern void nouveau_ramht_ref(struct nouveau_ramht *, struct nouveau_ramht **,
+ struct nouveau_channel *unref_channel);
+
+extern int nouveau_ramht_insert(struct nouveau_channel *, u32 handle,
+ struct nouveau_gpuobj *);
+extern void nouveau_ramht_remove(struct nouveau_channel *, u32 handle);
+extern struct nouveau_gpuobj *
+nouveau_ramht_find(struct nouveau_channel *chan, u32 handle);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index 21a6e453b975..1b42541ca9e5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -551,6 +551,8 @@
#define NV10_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000324C
#define NV03_PFIFO_CACHE1_PULL0 0x00003240
#define NV04_PFIFO_CACHE1_PULL0 0x00003250
+# define NV04_PFIFO_CACHE1_PULL0_HASH_FAILED 0x00000010
+# define NV04_PFIFO_CACHE1_PULL0_HASH_BUSY 0x00001000
#define NV03_PFIFO_CACHE1_PULL1 0x00003250
#define NV04_PFIFO_CACHE1_PULL1 0x00003254
#define NV04_PFIFO_CACHE1_HASH 0x00003258
@@ -785,15 +787,12 @@
#define NV50_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
#define NV50_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610b70 + (i) * 0x8)
#define NV50_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610b74 + (i) * 0x8)
+#define NV50_PDISPLAY_EXT_MODE_CTRL_P(i) (0x00610b80 + (i) * 0x8)
+#define NV50_PDISPLAY_EXT_MODE_CTRL_C(i) (0x00610b84 + (i) * 0x8)
#define NV50_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610bdc + (i) * 0x8)
#define NV50_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610be0 + (i) * 0x8)
-
#define NV90_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610794 + (i) * 0x8)
#define NV90_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610798 + (i) * 0x8)
-#define NV90_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8)
-#define NV90_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
-#define NV90_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610b80 + (i) * 0x8)
-#define NV90_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610b84 + (i) * 0x8)
#define NV50_PDISPLAY_CRTC_CLK 0x00614000
#define NV50_PDISPLAY_CRTC_CLK_CTRL1(i) ((i) * 0x800 + 0x614100)
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index 6b9187d7f67d..288bacac7e5a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -95,9 +95,9 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
unsigned i, j, pte;
- NV_DEBUG(dev, "pg=0x%lx\n", mem->mm_node->start);
+ NV_DEBUG(dev, "pg=0x%lx\n", mem->start);
- pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT);
+ pte = nouveau_sgdma_pte(nvbe->dev, mem->start << PAGE_SHIFT);
nvbe->pte_start = pte;
for (i = 0; i < nvbe->nr_pages; i++) {
dma_addr_t dma_offset = nvbe->pages[i];
@@ -105,11 +105,13 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
uint32_t offset_h = upper_32_bits(dma_offset);
for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
- if (dev_priv->card_type < NV_50)
- nv_wo32(dev, gpuobj, pte++, offset_l | 3);
- else {
- nv_wo32(dev, gpuobj, pte++, offset_l | 0x21);
- nv_wo32(dev, gpuobj, pte++, offset_h & 0xff);
+ if (dev_priv->card_type < NV_50) {
+ nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 3);
+ pte += 1;
+ } else {
+ nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 0x21);
+ nv_wo32(gpuobj, (pte * 4) + 4, offset_h & 0xff);
+ pte += 2;
}
dma_offset += NV_CTXDMA_PAGE_SIZE;
@@ -145,11 +147,13 @@ nouveau_sgdma_unbind(struct ttm_backend *be)
dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus;
for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
- if (dev_priv->card_type < NV_50)
- nv_wo32(dev, gpuobj, pte++, dma_offset | 3);
- else {
- nv_wo32(dev, gpuobj, pte++, dma_offset | 0x21);
- nv_wo32(dev, gpuobj, pte++, 0x00000000);
+ if (dev_priv->card_type < NV_50) {
+ nv_wo32(gpuobj, (pte * 4) + 0, dma_offset | 3);
+ pte += 1;
+ } else {
+ nv_wo32(gpuobj, (pte * 4) + 0, 0x00000000);
+ nv_wo32(gpuobj, (pte * 4) + 4, 0x00000000);
+ pte += 2;
}
dma_offset += NV_CTXDMA_PAGE_SIZE;
@@ -230,7 +234,6 @@ nouveau_sgdma_init(struct drm_device *dev)
}
ret = nouveau_gpuobj_new(dev, NULL, obj_size, 16,
- NVOBJ_FLAG_ALLOW_NO_REFS |
NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE, &gpuobj);
if (ret) {
@@ -239,9 +242,9 @@ nouveau_sgdma_init(struct drm_device *dev)
}
dev_priv->gart_info.sg_dummy_page =
- alloc_page(GFP_KERNEL|__GFP_DMA32);
+ alloc_page(GFP_KERNEL|__GFP_DMA32|__GFP_ZERO);
if (!dev_priv->gart_info.sg_dummy_page) {
- nouveau_gpuobj_del(dev, &gpuobj);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
return -ENOMEM;
}
@@ -250,29 +253,34 @@ nouveau_sgdma_init(struct drm_device *dev)
pci_map_page(pdev, dev_priv->gart_info.sg_dummy_page, 0,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(pdev, dev_priv->gart_info.sg_dummy_bus)) {
- nouveau_gpuobj_del(dev, &gpuobj);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
return -EFAULT;
}
if (dev_priv->card_type < NV_50) {
+ /* special case, allocated from global instmem heap so
+ * cinst is invalid, we use it on all channels though so
+ * cinst needs to be valid, set it the same as pinst
+ */
+ gpuobj->cinst = gpuobj->pinst;
+
/* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and
* confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE
* on those cards? */
- nv_wo32(dev, gpuobj, 0, NV_CLASS_DMA_IN_MEMORY |
- (1 << 12) /* PT present */ |
- (0 << 13) /* PT *not* linear */ |
- (NV_DMA_ACCESS_RW << 14) |
- (NV_DMA_TARGET_PCI << 16));
- nv_wo32(dev, gpuobj, 1, aper_size - 1);
+ nv_wo32(gpuobj, 0, NV_CLASS_DMA_IN_MEMORY |
+ (1 << 12) /* PT present */ |
+ (0 << 13) /* PT *not* linear */ |
+ (NV_DMA_ACCESS_RW << 14) |
+ (NV_DMA_TARGET_PCI << 16));
+ nv_wo32(gpuobj, 4, aper_size - 1);
for (i = 2; i < 2 + (aper_size >> 12); i++) {
- nv_wo32(dev, gpuobj, i,
- dev_priv->gart_info.sg_dummy_bus | 3);
+ nv_wo32(gpuobj, i * 4,
+ dev_priv->gart_info.sg_dummy_bus | 3);
}
} else {
for (i = 0; i < obj_size; i += 8) {
- nv_wo32(dev, gpuobj, (i+0)/4,
- dev_priv->gart_info.sg_dummy_bus | 0x21);
- nv_wo32(dev, gpuobj, (i+4)/4, 0);
+ nv_wo32(gpuobj, i + 0, 0x00000000);
+ nv_wo32(gpuobj, i + 4, 0x00000000);
}
}
dev_priv->engine.instmem.flush(dev);
@@ -298,7 +306,7 @@ nouveau_sgdma_takedown(struct drm_device *dev)
dev_priv->gart_info.sg_dummy_bus = 0;
}
- nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma);
+ nouveau_gpuobj_ref(NULL, &dev_priv->gart_info.sg_ctxdma);
}
int
@@ -308,9 +316,9 @@ nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page)
struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
int pte;
- pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
+ pte = (offset >> NV_CTXDMA_PAGE_SHIFT) << 2;
if (dev_priv->card_type < NV_50) {
- *page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK;
+ *page = nv_ro32(gpuobj, (pte + 8)) & ~NV_CTXDMA_PAGE_MASK;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 989322be3728..ed7757f14083 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -35,6 +35,8 @@
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_fbcon.h"
+#include "nouveau_ramht.h"
+#include "nouveau_pm.h"
#include "nv50_display.h"
static void nouveau_stub_takedown(struct drm_device *dev) {}
@@ -78,7 +80,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
- engine->fifo.cache_flush = nv04_fifo_cache_flush;
engine->fifo.cache_pull = nv04_fifo_cache_pull;
engine->fifo.channel_id = nv04_fifo_channel_id;
engine->fifo.create_context = nv04_fifo_create_context;
@@ -95,6 +96,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = NULL;
engine->gpio.set = NULL;
engine->gpio.irq_enable = NULL;
+ engine->pm.clock_get = nv04_pm_clock_get;
+ engine->pm.clock_pre = nv04_pm_clock_pre;
+ engine->pm.clock_set = nv04_pm_clock_set;
break;
case 0x10:
engine->instmem.init = nv04_instmem_init;
@@ -130,7 +134,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
- engine->fifo.cache_flush = nv04_fifo_cache_flush;
engine->fifo.cache_pull = nv04_fifo_cache_pull;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv10_fifo_create_context;
@@ -147,6 +150,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = nv10_gpio_get;
engine->gpio.set = nv10_gpio_set;
engine->gpio.irq_enable = NULL;
+ engine->pm.clock_get = nv04_pm_clock_get;
+ engine->pm.clock_pre = nv04_pm_clock_pre;
+ engine->pm.clock_set = nv04_pm_clock_set;
break;
case 0x20:
engine->instmem.init = nv04_instmem_init;
@@ -182,7 +188,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
- engine->fifo.cache_flush = nv04_fifo_cache_flush;
engine->fifo.cache_pull = nv04_fifo_cache_pull;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv10_fifo_create_context;
@@ -199,6 +204,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = nv10_gpio_get;
engine->gpio.set = nv10_gpio_set;
engine->gpio.irq_enable = NULL;
+ engine->pm.clock_get = nv04_pm_clock_get;
+ engine->pm.clock_pre = nv04_pm_clock_pre;
+ engine->pm.clock_set = nv04_pm_clock_set;
break;
case 0x30:
engine->instmem.init = nv04_instmem_init;
@@ -234,7 +242,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
- engine->fifo.cache_flush = nv04_fifo_cache_flush;
engine->fifo.cache_pull = nv04_fifo_cache_pull;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv10_fifo_create_context;
@@ -251,6 +258,11 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = nv10_gpio_get;
engine->gpio.set = nv10_gpio_set;
engine->gpio.irq_enable = NULL;
+ engine->pm.clock_get = nv04_pm_clock_get;
+ engine->pm.clock_pre = nv04_pm_clock_pre;
+ engine->pm.clock_set = nv04_pm_clock_set;
+ engine->pm.voltage_get = nouveau_voltage_gpio_get;
+ engine->pm.voltage_set = nouveau_voltage_gpio_set;
break;
case 0x40:
case 0x60:
@@ -287,7 +299,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
- engine->fifo.cache_flush = nv04_fifo_cache_flush;
engine->fifo.cache_pull = nv04_fifo_cache_pull;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv40_fifo_create_context;
@@ -304,6 +315,12 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = nv10_gpio_get;
engine->gpio.set = nv10_gpio_set;
engine->gpio.irq_enable = NULL;
+ engine->pm.clock_get = nv04_pm_clock_get;
+ engine->pm.clock_pre = nv04_pm_clock_pre;
+ engine->pm.clock_set = nv04_pm_clock_set;
+ engine->pm.voltage_get = nouveau_voltage_gpio_get;
+ engine->pm.voltage_set = nouveau_voltage_gpio_set;
+ engine->pm.temp_get = nv40_temp_get;
break;
case 0x50:
case 0x80: /* gotta love NVIDIA's consistency.. */
@@ -358,6 +375,27 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = nv50_gpio_get;
engine->gpio.set = nv50_gpio_set;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
+ switch (dev_priv->chipset) {
+ case 0xa3:
+ case 0xa5:
+ case 0xa8:
+ case 0xaf:
+ engine->pm.clock_get = nva3_pm_clock_get;
+ engine->pm.clock_pre = nva3_pm_clock_pre;
+ engine->pm.clock_set = nva3_pm_clock_set;
+ break;
+ default:
+ engine->pm.clock_get = nv50_pm_clock_get;
+ engine->pm.clock_pre = nv50_pm_clock_pre;
+ engine->pm.clock_set = nv50_pm_clock_set;
+ break;
+ }
+ engine->pm.voltage_get = nouveau_voltage_gpio_get;
+ engine->pm.voltage_set = nouveau_voltage_gpio_set;
+ if (dev_priv->chipset >= 0x84)
+ engine->pm.temp_get = nv84_temp_get;
+ else
+ engine->pm.temp_get = nv40_temp_get;
break;
case 0xC0:
engine->instmem.init = nvc0_instmem_init;
@@ -437,16 +475,14 @@ static int
nouveau_card_init_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *gpuobj;
+ struct nouveau_gpuobj *gpuobj = NULL;
int ret;
ret = nouveau_channel_alloc(dev, &dev_priv->channel,
- (struct drm_file *)-2,
- NvDmaFB, NvDmaTT);
+ (struct drm_file *)-2, NvDmaFB, NvDmaTT);
if (ret)
return ret;
- gpuobj = NULL;
ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY,
0, dev_priv->vram_size,
NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM,
@@ -454,26 +490,25 @@ nouveau_card_init_channel(struct drm_device *dev)
if (ret)
goto out_err;
- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaVRAM,
- gpuobj, NULL);
+ ret = nouveau_ramht_insert(dev_priv->channel, NvDmaVRAM, gpuobj);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
if (ret)
goto out_err;
- gpuobj = NULL;
ret = nouveau_gpuobj_gart_dma_new(dev_priv->channel, 0,
dev_priv->gart_info.aper_size,
NV_DMA_ACCESS_RW, &gpuobj, NULL);
if (ret)
goto out_err;
- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaGART,
- gpuobj, NULL);
+ ret = nouveau_ramht_insert(dev_priv->channel, NvDmaGART, gpuobj);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
if (ret)
goto out_err;
return 0;
+
out_err:
- nouveau_gpuobj_del(dev, &gpuobj);
nouveau_channel_free(dev_priv->channel);
dev_priv->channel = NULL;
return ret;
@@ -534,35 +569,28 @@ nouveau_card_init(struct drm_device *dev)
if (ret)
goto out_display_early;
- ret = nouveau_mem_detect(dev);
+ nouveau_pm_init(dev);
+
+ ret = nouveau_mem_vram_init(dev);
if (ret)
goto out_bios;
- ret = nouveau_gpuobj_early_init(dev);
+ ret = nouveau_gpuobj_init(dev);
if (ret)
- goto out_bios;
+ goto out_vram;
- /* Initialise instance memory, must happen before mem_init so we
- * know exactly how much VRAM we're able to use for "normal"
- * purposes.
- */
ret = engine->instmem.init(dev);
if (ret)
- goto out_gpuobj_early;
+ goto out_gpuobj;
- /* Setup the memory manager */
- ret = nouveau_mem_init(dev);
+ ret = nouveau_mem_gart_init(dev);
if (ret)
goto out_instmem;
- ret = nouveau_gpuobj_init(dev);
- if (ret)
- goto out_mem;
-
/* PMC */
ret = engine->mc.init(dev);
if (ret)
- goto out_gpuobj;
+ goto out_gart;
/* PGPIO */
ret = engine->gpio.init(dev);
@@ -611,9 +639,13 @@ nouveau_card_init(struct drm_device *dev)
/* what about PVIDEO/PCRTC/PRAMDAC etc? */
if (!engine->graph.accel_blocked) {
- ret = nouveau_card_init_channel(dev);
+ ret = nouveau_fence_init(dev);
if (ret)
goto out_irq;
+
+ ret = nouveau_card_init_channel(dev);
+ if (ret)
+ goto out_fence;
}
ret = nouveau_backlight_init(dev);
@@ -624,6 +656,8 @@ nouveau_card_init(struct drm_device *dev)
drm_kms_helper_poll_init(dev);
return 0;
+out_fence:
+ nouveau_fence_fini(dev);
out_irq:
drm_irq_uninstall(dev);
out_display:
@@ -642,16 +676,16 @@ out_gpio:
engine->gpio.takedown(dev);
out_mc:
engine->mc.takedown(dev);
-out_gpuobj:
- nouveau_gpuobj_takedown(dev);
-out_mem:
- nouveau_sgdma_takedown(dev);
- nouveau_mem_close(dev);
+out_gart:
+ nouveau_mem_gart_fini(dev);
out_instmem:
engine->instmem.takedown(dev);
-out_gpuobj_early:
- nouveau_gpuobj_late_takedown(dev);
+out_gpuobj:
+ nouveau_gpuobj_takedown(dev);
+out_vram:
+ nouveau_mem_vram_fini(dev);
out_bios:
+ nouveau_pm_fini(dev);
nouveau_bios_takedown(dev);
out_display_early:
engine->display.late_takedown(dev);
@@ -667,7 +701,8 @@ static void nouveau_card_takedown(struct drm_device *dev)
nouveau_backlight_exit(dev);
- if (dev_priv->channel) {
+ if (!engine->graph.accel_blocked) {
+ nouveau_fence_fini(dev);
nouveau_channel_free(dev_priv->channel);
dev_priv->channel = NULL;
}
@@ -686,15 +721,15 @@ static void nouveau_card_takedown(struct drm_device *dev)
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
mutex_unlock(&dev->struct_mutex);
- nouveau_sgdma_takedown(dev);
+ nouveau_mem_gart_fini(dev);
- nouveau_gpuobj_takedown(dev);
- nouveau_mem_close(dev);
engine->instmem.takedown(dev);
+ nouveau_gpuobj_takedown(dev);
+ nouveau_mem_vram_fini(dev);
drm_irq_uninstall(dev);
- nouveau_gpuobj_late_takedown(dev);
+ nouveau_pm_fini(dev);
nouveau_bios_takedown(dev);
vga_client_register(dev->pdev, NULL, NULL, NULL);
@@ -1057,7 +1092,7 @@ bool nouveau_wait_until(struct drm_device *dev, uint64_t timeout,
/* Waits for PGRAPH to go completely idle */
bool nouveau_wait_for_idle(struct drm_device *dev)
{
- if (!nv_wait(NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) {
+ if (!nv_wait(dev, NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) {
NV_ERROR(dev, "PGRAPH idle timed out with status 0x%08x\n",
nv_rd32(dev, NV04_PGRAPH_STATUS));
return false;
diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c
new file mode 100644
index 000000000000..16bbbf1eff63
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_temp.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2010 PathScale 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: Martin Peres
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_pm.h"
+
+static void
+nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants;
+ struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp;
+ int i, headerlen, recordlen, entries;
+
+ if (!temp) {
+ NV_DEBUG(dev, "temperature table pointer invalid\n");
+ return;
+ }
+
+ /* Set the default sensor's contants */
+ sensor->offset_constant = 0;
+ sensor->offset_mult = 1;
+ sensor->offset_div = 1;
+ sensor->slope_mult = 1;
+ sensor->slope_div = 1;
+
+ /* Set the default temperature thresholds */
+ temps->critical = 110;
+ temps->down_clock = 100;
+ temps->fan_boost = 90;
+
+ /* Set the known default values to setup the temperature sensor */
+ if (dev_priv->card_type >= NV_40) {
+ switch (dev_priv->chipset) {
+ case 0x43:
+ sensor->offset_mult = 32060;
+ sensor->offset_div = 1000;
+ sensor->slope_mult = 792;
+ sensor->slope_div = 1000;
+ break;
+
+ case 0x44:
+ case 0x47:
+ case 0x4a:
+ sensor->offset_mult = 27839;
+ sensor->offset_div = 1000;
+ sensor->slope_mult = 780;
+ sensor->slope_div = 1000;
+ break;
+
+ case 0x46:
+ sensor->offset_mult = -24775;
+ sensor->offset_div = 100;
+ sensor->slope_mult = 467;
+ sensor->slope_div = 10000;
+ break;
+
+ case 0x49:
+ sensor->offset_mult = -25051;
+ sensor->offset_div = 100;
+ sensor->slope_mult = 458;
+ sensor->slope_div = 10000;
+ break;
+
+ case 0x4b:
+ sensor->offset_mult = -24088;
+ sensor->offset_div = 100;
+ sensor->slope_mult = 442;
+ sensor->slope_div = 10000;
+ break;
+
+ case 0x50:
+ sensor->offset_mult = -22749;
+ sensor->offset_div = 100;
+ sensor->slope_mult = 431;
+ sensor->slope_div = 10000;
+ break;
+ }
+ }
+
+ headerlen = temp[1];
+ recordlen = temp[2];
+ entries = temp[3];
+ temp = temp + headerlen;
+
+ /* Read the entries from the table */
+ for (i = 0; i < entries; i++) {
+ u16 value = ROM16(temp[1]);
+
+ switch (temp[0]) {
+ case 0x01:
+ if ((value & 0x8f) == 0)
+ sensor->offset_constant = (value >> 9) & 0x7f;
+ break;
+
+ case 0x04:
+ if ((value & 0xf00f) == 0xa000) /* core */
+ temps->critical = (value&0x0ff0) >> 4;
+ break;
+
+ case 0x07:
+ if ((value & 0xf00f) == 0xa000) /* core */
+ temps->down_clock = (value&0x0ff0) >> 4;
+ break;
+
+ case 0x08:
+ if ((value & 0xf00f) == 0xa000) /* core */
+ temps->fan_boost = (value&0x0ff0) >> 4;
+ break;
+
+ case 0x10:
+ sensor->offset_mult = value;
+ break;
+
+ case 0x11:
+ sensor->offset_div = value;
+ break;
+
+ case 0x12:
+ sensor->slope_mult = value;
+ break;
+
+ case 0x13:
+ sensor->slope_div = value;
+ break;
+ }
+ temp += recordlen;
+ }
+
+ nouveau_temp_safety_checks(dev);
+}
+
+static int
+nv40_sensor_setup(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_temp_sensor_constants *sensor = &pm->sensor_constants;
+ u32 offset = sensor->offset_mult / sensor->offset_div;
+ u32 sensor_calibration;
+
+ /* set up the sensors */
+ sensor_calibration = 120 - offset - sensor->offset_constant;
+ sensor_calibration = sensor_calibration * sensor->slope_div /
+ sensor->slope_mult;
+
+ if (dev_priv->chipset >= 0x46)
+ sensor_calibration |= 0x80000000;
+ else
+ sensor_calibration |= 0x10000000;
+
+ nv_wr32(dev, 0x0015b0, sensor_calibration);
+
+ /* Wait for the sensor to update */
+ msleep(5);
+
+ /* read */
+ return nv_rd32(dev, 0x0015b4) & 0x1fff;
+}
+
+int
+nv40_temp_get(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_temp_sensor_constants *sensor = &pm->sensor_constants;
+ int offset = sensor->offset_mult / sensor->offset_div;
+ int core_temp;
+
+ if (dev_priv->chipset >= 0x50) {
+ core_temp = nv_rd32(dev, 0x20008);
+ } else {
+ core_temp = nv_rd32(dev, 0x0015b4) & 0x1fff;
+ /* Setup the sensor if the temperature is 0 */
+ if (core_temp == 0)
+ core_temp = nv40_sensor_setup(dev);
+ }
+
+ core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
+ core_temp = core_temp + offset + sensor->offset_constant;
+
+ return core_temp;
+}
+
+int
+nv84_temp_get(struct drm_device *dev)
+{
+ return nv_rd32(dev, 0x20400);
+}
+
+void
+nouveau_temp_safety_checks(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_threshold_temp *temps = &pm->threshold_temp;
+
+ if (temps->critical > 120)
+ temps->critical = 120;
+ else if (temps->critical < 80)
+ temps->critical = 80;
+
+ if (temps->down_clock > 110)
+ temps->down_clock = 110;
+ else if (temps->down_clock < 60)
+ temps->down_clock = 60;
+
+ if (temps->fan_boost > 100)
+ temps->fan_boost = 100;
+ else if (temps->fan_boost < 40)
+ temps->fan_boost = 40;
+}
+
+static bool
+probe_monitoring_device(struct nouveau_i2c_chan *i2c,
+ struct i2c_board_info *info)
+{
+ char modalias[16] = "i2c:";
+ struct i2c_client *client;
+
+ strlcat(modalias, info->type, sizeof(modalias));
+ request_module(modalias);
+
+ client = i2c_new_device(&i2c->adapter, info);
+ if (!client)
+ return false;
+
+ if (!client->driver || client->driver->detect(client, info)) {
+ i2c_unregister_device(client);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+nouveau_temp_probe_i2c(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct dcb_table *dcb = &dev_priv->vbios.dcb;
+ struct i2c_board_info info[] = {
+ { I2C_BOARD_INFO("w83l785ts", 0x2d) },
+ { I2C_BOARD_INFO("w83781d", 0x2d) },
+ { I2C_BOARD_INFO("f75375", 0x2e) },
+ { I2C_BOARD_INFO("adt7473", 0x2e) },
+ { I2C_BOARD_INFO("lm99", 0x4c) },
+ { }
+ };
+ int idx = (dcb->version >= 0x40 ?
+ dcb->i2c_default_indices & 0xf : 2);
+
+ nouveau_i2c_identify(dev, "monitoring device", info,
+ probe_monitoring_device, idx);
+}
+
+void
+nouveau_temp_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct bit_entry P;
+ u8 *temp = NULL;
+
+ if (bios->type == NVBIOS_BIT) {
+ if (bit_table(dev, 'P', &P))
+ return;
+
+ if (P.version == 1)
+ temp = ROMPTR(bios, P.data[12]);
+ else if (P.version == 2)
+ temp = ROMPTR(bios, P.data[16]);
+ else
+ NV_WARN(dev, "unknown temp for BIT P %d\n", P.version);
+
+ nouveau_temp_vbios_parse(dev, temp);
+ }
+
+ nouveau_temp_probe_i2c(dev);
+}
+
+void
+nouveau_temp_fini(struct drm_device *dev)
+{
+
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c
new file mode 100644
index 000000000000..04fdc00a67d5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_volt.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_pm.h"
+
+static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a };
+static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]);
+
+int
+nouveau_voltage_gpio_get(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio;
+ struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage;
+ u8 vid = 0;
+ int i;
+
+ for (i = 0; i < nr_vidtag; i++) {
+ if (!(volt->vid_mask & (1 << i)))
+ continue;
+
+ vid |= gpio->get(dev, vidtag[i]) << i;
+ }
+
+ return nouveau_volt_lvl_lookup(dev, vid);
+}
+
+int
+nouveau_voltage_gpio_set(struct drm_device *dev, int voltage)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio;
+ struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage;
+ int vid, i;
+
+ vid = nouveau_volt_vid_lookup(dev, voltage);
+ if (vid < 0)
+ return vid;
+
+ for (i = 0; i < nr_vidtag; i++) {
+ if (!(volt->vid_mask & (1 << i)))
+ continue;
+
+ gpio->set(dev, vidtag[i], !!(vid & (1 << i)));
+ }
+
+ return 0;
+}
+
+int
+nouveau_volt_vid_lookup(struct drm_device *dev, int voltage)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage;
+ int i;
+
+ for (i = 0; i < volt->nr_level; i++) {
+ if (volt->level[i].voltage == voltage)
+ return volt->level[i].vid;
+ }
+
+ return -ENOENT;
+}
+
+int
+nouveau_volt_lvl_lookup(struct drm_device *dev, int vid)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage;
+ int i;
+
+ for (i = 0; i < volt->nr_level; i++) {
+ if (volt->level[i].vid == vid)
+ return volt->level[i].voltage;
+ }
+
+ return -ENOENT;
+}
+
+void
+nouveau_volt_init(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_voltage *voltage = &pm->voltage;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct bit_entry P;
+ u8 *volt = NULL, *entry;
+ int i, headerlen, recordlen, entries, vidmask, vidshift;
+
+ if (bios->type == NVBIOS_BIT) {
+ if (bit_table(dev, 'P', &P))
+ return;
+
+ if (P.version == 1)
+ volt = ROMPTR(bios, P.data[16]);
+ else
+ if (P.version == 2)
+ volt = ROMPTR(bios, P.data[12]);
+ else {
+ NV_WARN(dev, "unknown volt for BIT P %d\n", P.version);
+ }
+ } else {
+ if (bios->data[bios->offset + 6] < 0x27) {
+ NV_DEBUG(dev, "BMP version too old for voltage\n");
+ return;
+ }
+
+ volt = ROMPTR(bios, bios->data[bios->offset + 0x98]);
+ }
+
+ if (!volt) {
+ NV_DEBUG(dev, "voltage table pointer invalid\n");
+ return;
+ }
+
+ switch (volt[0]) {
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ headerlen = 5;
+ recordlen = volt[1];
+ entries = volt[2];
+ vidshift = 0;
+ vidmask = volt[4];
+ break;
+ case 0x20:
+ headerlen = volt[1];
+ recordlen = volt[3];
+ entries = volt[2];
+ vidshift = 0; /* could be vidshift like 0x30? */
+ vidmask = volt[5];
+ break;
+ case 0x30:
+ headerlen = volt[1];
+ recordlen = volt[2];
+ entries = volt[3];
+ vidshift = hweight8(volt[5]);
+ vidmask = volt[4];
+ break;
+ default:
+ NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]);
+ return;
+ }
+
+ /* validate vid mask */
+ voltage->vid_mask = vidmask;
+ if (!voltage->vid_mask)
+ return;
+
+ i = 0;
+ while (vidmask) {
+ if (i > nr_vidtag) {
+ NV_DEBUG(dev, "vid bit %d unknown\n", i);
+ return;
+ }
+
+ if (!nouveau_bios_gpio_entry(dev, vidtag[i])) {
+ NV_DEBUG(dev, "vid bit %d has no gpio tag\n", i);
+ return;
+ }
+
+ vidmask >>= 1;
+ i++;
+ }
+
+ /* parse vbios entries into common format */
+ voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);
+ if (!voltage->level)
+ return;
+
+ entry = volt + headerlen;
+ for (i = 0; i < entries; i++, entry += recordlen) {
+ voltage->level[i].voltage = entry[0];
+ voltage->level[i].vid = entry[1] >> vidshift;
+ }
+ voltage->nr_level = entries;
+ voltage->supported = true;
+}
+
+void
+nouveau_volt_fini(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage;
+
+ kfree(volt->level);
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 497df8765f28..c71abc2a34d5 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -33,6 +33,7 @@
#include "nouveau_fb.h"
#include "nouveau_hw.h"
#include "nvreg.h"
+#include "nouveau_fbcon.h"
static int
nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
@@ -109,7 +110,7 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
struct nouveau_pll_vals *pv = &regp->pllvals;
struct pll_lims pll_lim;
- if (get_pll_limits(dev, nv_crtc->index ? VPLL2 : VPLL1, &pll_lim))
+ if (get_pll_limits(dev, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0, &pll_lim))
return;
/* NM2 == 0 is used to determine single stage mode on two stage plls */
@@ -718,6 +719,7 @@ static void nv_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
+ nouveau_bo_unmap(nv_crtc->cursor.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
kfree(nv_crtc);
}
@@ -768,8 +770,9 @@ nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start,
}
static int
-nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
- struct drm_framebuffer *old_fb)
+nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *passed_fb,
+ int x, int y, bool atomic)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct drm_device *dev = crtc->dev;
@@ -780,13 +783,26 @@ nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
int arb_burst, arb_lwm;
int ret;
- ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
- if (ret)
- return ret;
+ /* If atomic, we want to switch to the fb we were passed, so
+ * now we update pointers to do that. (We don't pin; just
+ * assume we're already pinned and update the base address.)
+ */
+ if (atomic) {
+ drm_fb = passed_fb;
+ fb = nouveau_framebuffer(passed_fb);
+ }
+ else {
+ /* If not atomic, we can go ahead and pin, and unpin the
+ * old fb we were passed.
+ */
+ ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
+ if (ret)
+ return ret;
- if (old_fb) {
- struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
- nouveau_bo_unpin(ofb->nvbo);
+ if (passed_fb) {
+ struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb);
+ nouveau_bo_unpin(ofb->nvbo);
+ }
}
nv_crtc->fb.offset = fb->nvbo->bo.offset;
@@ -826,7 +842,7 @@ nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX);
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX);
- if (dev_priv->card_type >= NV_30) {
+ if (dev_priv->card_type >= NV_20) {
regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8;
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47);
}
@@ -834,6 +850,29 @@ nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
}
+static int
+nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
+}
+
+static int
+nv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, enum mode_set_atomic state)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ struct drm_device *dev = dev_priv->dev;
+
+ if (state == ENTER_ATOMIC_MODE_SET)
+ nouveau_fbcon_save_disable_accel(dev);
+ else
+ nouveau_fbcon_restore_accel(dev);
+
+ return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true);
+}
+
static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
struct nouveau_bo *dst)
{
@@ -962,6 +1001,7 @@ static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = {
.mode_fixup = nv_crtc_mode_fixup,
.mode_set = nv_crtc_mode_set,
.mode_set_base = nv04_crtc_mode_set_base,
+ .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic,
.load_lut = nv_crtc_gamma_load,
};
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
index ea3627041ecf..ba6423f2ffcc 100644
--- a/drivers/gpu/drm/nouveau/nv04_dac.c
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
@@ -291,6 +291,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
msleep(5);
sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+ /* do it again just in case it's a residual current */
+ sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
@@ -343,22 +345,13 @@ static void nv04_dac_prepare(struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
struct drm_device *dev = encoder->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
int head = nouveau_crtc(encoder->crtc)->index;
- struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
helper->dpms(encoder, DRM_MODE_DPMS_OFF);
nv04_dfp_disable(dev, head);
-
- /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
- * at LCD__INDEX which we don't alter
- */
- if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44))
- crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
}
-
static void nv04_dac_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
index 0d3206a7046c..c936403b26e2 100644
--- a/drivers/gpu/drm/nouveau/nv04_dfp.c
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -104,6 +104,8 @@ void nv04_dfp_disable(struct drm_device *dev, int head)
}
/* don't inadvertently turn it on when state written later */
crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
+ crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] &=
+ ~NV_CIO_CRE_LCD_ROUTE_MASK;
}
void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
@@ -253,26 +255,21 @@ static void nv04_dfp_prepare(struct drm_encoder *encoder)
nv04_dfp_prepare_sel_clk(dev, nv_encoder, head);
- /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
- * at LCD__INDEX which we don't alter
- */
- if (!(*cr_lcd & 0x44)) {
- *cr_lcd = 0x3;
-
- if (nv_two_heads(dev)) {
- if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
- *cr_lcd |= head ? 0x0 : 0x8;
- else {
- *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
- if (nv_encoder->dcb->type == OUTPUT_LVDS)
- *cr_lcd |= 0x30;
- if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
- /* avoid being connected to both crtcs */
- *cr_lcd_oth &= ~0x30;
- NVWriteVgaCrtc(dev, head ^ 1,
- NV_CIO_CRE_LCD__INDEX,
- *cr_lcd_oth);
- }
+ *cr_lcd = (*cr_lcd & ~NV_CIO_CRE_LCD_ROUTE_MASK) | 0x3;
+
+ if (nv_two_heads(dev)) {
+ if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
+ *cr_lcd |= head ? 0x0 : 0x8;
+ else {
+ *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
+ if (nv_encoder->dcb->type == OUTPUT_LVDS)
+ *cr_lcd |= 0x30;
+ if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
+ /* avoid being connected to both crtcs */
+ *cr_lcd_oth &= ~0x30;
+ NVWriteVgaCrtc(dev, head ^ 1,
+ NV_CIO_CRE_LCD__INDEX,
+ *cr_lcd_oth);
}
}
}
@@ -640,7 +637,7 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder)
get_tmds_slave(encoder))
return;
- type = nouveau_i2c_identify(dev, "TMDS transmitter", info, 2);
+ type = nouveau_i2c_identify(dev, "TMDS transmitter", info, NULL, 2);
if (type < 0)
return;
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
index 1eeac4fae73d..33e4c9388bc1 100644
--- a/drivers/gpu/drm/nouveau/nv04_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
@@ -25,6 +25,7 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
+#include "nouveau_ramht.h"
#include "nouveau_fbcon.h"
void
@@ -169,11 +170,9 @@ nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle)
if (ret)
return ret;
- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL);
- if (ret)
- return ret;
-
- return 0;
+ ret = nouveau_ramht_insert(dev_priv->channel, handle, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ return ret;
}
int
diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c
index 06cedd99c26a..708293b7ddcd 100644
--- a/drivers/gpu/drm/nouveau/nv04_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv04_fifo.c
@@ -27,8 +27,9 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
-#define NV04_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV04_RAMFC__SIZE))
+#define NV04_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV04_RAMFC__SIZE))
#define NV04_RAMFC__SIZE 32
#define NV04_RAMFC_DMA_PUT 0x00
#define NV04_RAMFC_DMA_GET 0x04
@@ -38,10 +39,8 @@
#define NV04_RAMFC_ENGINE 0x14
#define NV04_RAMFC_PULL1_ENGINE 0x18
-#define RAMFC_WR(offset, val) nv_wo32(dev, chan->ramfc->gpuobj, \
- NV04_RAMFC_##offset/4, (val))
-#define RAMFC_RD(offset) nv_ro32(dev, chan->ramfc->gpuobj, \
- NV04_RAMFC_##offset/4)
+#define RAMFC_WR(offset, val) nv_wo32(chan->ramfc, NV04_RAMFC_##offset, (val))
+#define RAMFC_RD(offset) nv_ro32(chan->ramfc, NV04_RAMFC_##offset)
void
nv04_fifo_disable(struct drm_device *dev)
@@ -72,37 +71,32 @@ nv04_fifo_reassign(struct drm_device *dev, bool enable)
}
bool
-nv04_fifo_cache_flush(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
- uint64_t start = ptimer->read(dev);
-
- do {
- if (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) ==
- nv_rd32(dev, NV03_PFIFO_CACHE1_PUT))
- return true;
-
- } while (ptimer->read(dev) - start < 100000000);
-
- NV_ERROR(dev, "Timeout flushing the PFIFO cache.\n");
-
- return false;
-}
-
-bool
nv04_fifo_cache_pull(struct drm_device *dev, bool enable)
{
- uint32_t pull = nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0);
+ int pull = nv_mask(dev, NV04_PFIFO_CACHE1_PULL0, 1, enable);
+
+ if (!enable) {
+ /* In some cases the PFIFO puller may be left in an
+ * inconsistent state if you try to stop it when it's
+ * busy translating handles. Sometimes you get a
+ * PFIFO_CACHE_ERROR, sometimes it just fails silently
+ * sending incorrect instance offsets to PGRAPH after
+ * it's started up again. To avoid the latter we
+ * invalidate the most recently calculated instance.
+ */
+ if (!nv_wait(dev, NV04_PFIFO_CACHE1_PULL0,
+ NV04_PFIFO_CACHE1_PULL0_HASH_BUSY, 0))
+ NV_ERROR(dev, "Timeout idling the PFIFO puller.\n");
+
+ if (nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0) &
+ NV04_PFIFO_CACHE1_PULL0_HASH_FAILED)
+ nv_wr32(dev, NV03_PFIFO_INTR_0,
+ NV_PFIFO_INTR_CACHE_ERROR);
- if (enable) {
- nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull | 1);
- } else {
- nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull & ~1);
nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
}
- return !!(pull & 1);
+ return pull & 1;
}
int
@@ -130,7 +124,7 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
NV04_RAMFC__SIZE,
NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE,
- NULL, &chan->ramfc);
+ &chan->ramfc);
if (ret)
return ret;
@@ -139,7 +133,7 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
/* Setup initial state */
RAMFC_WR(DMA_PUT, chan->pushbuf_base);
RAMFC_WR(DMA_GET, chan->pushbuf_base);
- RAMFC_WR(DMA_INSTANCE, chan->pushbuf->instance >> 4);
+ RAMFC_WR(DMA_INSTANCE, chan->pushbuf->pinst >> 4);
RAMFC_WR(DMA_FETCH, (NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
@@ -161,7 +155,7 @@ nv04_fifo_destroy_context(struct nouveau_channel *chan)
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
- nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+ nouveau_gpuobj_ref(NULL, &chan->ramfc);
}
static void
@@ -264,10 +258,10 @@ nv04_fifo_init_ramxx(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
- ((dev_priv->ramht_bits - 9) << 16) |
- (dev_priv->ramht_offset >> 8));
- nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
- nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
+ ((dev_priv->ramht->bits - 9) << 16) |
+ (dev_priv->ramht->gpuobj->pinst >> 8));
+ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8);
+ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc->pinst >> 8);
}
static void
diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
index 4408232d33f1..0b5ae297abde 100644
--- a/drivers/gpu/drm/nouveau/nv04_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
@@ -1,6 +1,7 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
/* returns the size of fifo context */
static int
@@ -17,102 +18,51 @@ nouveau_fifo_ctx_size(struct drm_device *dev)
return 32;
}
-static void
-nv04_instmem_determine_amount(struct drm_device *dev)
+int nv04_instmem_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- int i;
+ struct nouveau_gpuobj *ramht = NULL;
+ u32 offset, length;
+ int ret;
- /* Figure out how much instance memory we need */
- if (dev_priv->card_type >= NV_40) {
- /* We'll want more instance memory than this on some NV4x cards.
- * There's a 16MB aperture to play with that maps onto the end
- * of vram. For now, only reserve a small piece until we know
- * more about what each chipset requires.
- */
- switch (dev_priv->chipset) {
- case 0x40:
- case 0x47:
- case 0x49:
- case 0x4b:
- dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
- break;
- default:
- dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
- break;
- }
- } else {
- /*XXX: what *are* the limits on <NV40 cards?
- */
- dev_priv->ramin_rsvd_vram = (512 * 1024);
- }
- NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10);
+ /* RAMIN always available */
+ dev_priv->ramin_available = true;
- /* Clear all of it, except the BIOS image that's in the first 64KiB */
- for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4)
- nv_wi32(dev, i, 0x00000000);
-}
+ /* Setup shared RAMHT */
+ ret = nouveau_gpuobj_new_fake(dev, 0x10000, ~0, 4096,
+ NVOBJ_FLAG_ZERO_ALLOC, &ramht);
+ if (ret)
+ return ret;
-static void
-nv04_instmem_configure_fixed_tables(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_engine *engine = &dev_priv->engine;
+ ret = nouveau_ramht_new(dev, ramht, &dev_priv->ramht);
+ nouveau_gpuobj_ref(NULL, &ramht);
+ if (ret)
+ return ret;
- /* FIFO hash table (RAMHT)
- * use 4k hash table at RAMIN+0x10000
- * TODO: extend the hash table
- */
- dev_priv->ramht_offset = 0x10000;
- dev_priv->ramht_bits = 9;
- dev_priv->ramht_size = (1 << dev_priv->ramht_bits); /* nr entries */
- dev_priv->ramht_size *= 8; /* 2 32-bit values per entry in RAMHT */
- NV_DEBUG(dev, "RAMHT offset=0x%x, size=%d\n", dev_priv->ramht_offset,
- dev_priv->ramht_size);
-
- /* FIFO runout table (RAMRO) - 512k at 0x11200 */
- dev_priv->ramro_offset = 0x11200;
- dev_priv->ramro_size = 512;
- NV_DEBUG(dev, "RAMRO offset=0x%x, size=%d\n", dev_priv->ramro_offset,
- dev_priv->ramro_size);
-
- /* FIFO context table (RAMFC)
- * NV40 : Not sure exactly how to position RAMFC on some cards,
- * 0x30002 seems to position it at RAMIN+0x20000 on these
- * cards. RAMFC is 4kb (32 fifos, 128byte entries).
- * Others: Position RAMFC at RAMIN+0x11400
- */
- dev_priv->ramfc_size = engine->fifo.channels *
- nouveau_fifo_ctx_size(dev);
+ /* And RAMRO */
+ ret = nouveau_gpuobj_new_fake(dev, 0x11200, ~0, 512,
+ NVOBJ_FLAG_ZERO_ALLOC, &dev_priv->ramro);
+ if (ret)
+ return ret;
+
+ /* And RAMFC */
+ length = dev_priv->engine.fifo.channels * nouveau_fifo_ctx_size(dev);
switch (dev_priv->card_type) {
case NV_40:
- dev_priv->ramfc_offset = 0x20000;
+ offset = 0x20000;
break;
- case NV_30:
- case NV_20:
- case NV_10:
- case NV_04:
default:
- dev_priv->ramfc_offset = 0x11400;
+ offset = 0x11400;
break;
}
- NV_DEBUG(dev, "RAMFC offset=0x%x, size=%d\n", dev_priv->ramfc_offset,
- dev_priv->ramfc_size);
-}
-int nv04_instmem_init(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t offset;
- int ret;
-
- nv04_instmem_determine_amount(dev);
- nv04_instmem_configure_fixed_tables(dev);
+ ret = nouveau_gpuobj_new_fake(dev, offset, ~0, length,
+ NVOBJ_FLAG_ZERO_ALLOC, &dev_priv->ramfc);
+ if (ret)
+ return ret;
- /* Create a heap to manage RAMIN allocations, we don't allocate
- * the space that was reserved for RAMHT/FC/RO.
- */
- offset = dev_priv->ramfc_offset + dev_priv->ramfc_size;
+ /* Only allow space after RAMFC to be used for object allocation */
+ offset += length;
/* It appears RAMRO (or something?) is controlled by 0x2220/0x2230
* on certain NV4x chipsets as well as RAMFC. When 0x2230 == 0
@@ -140,46 +90,34 @@ int nv04_instmem_init(struct drm_device *dev)
void
nv04_instmem_takedown(struct drm_device *dev)
{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ nouveau_ramht_ref(NULL, &dev_priv->ramht, NULL);
+ nouveau_gpuobj_ref(NULL, &dev_priv->ramro);
+ nouveau_gpuobj_ref(NULL, &dev_priv->ramfc);
}
int
-nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, uint32_t *sz)
+nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
+ uint32_t *sz)
{
- if (gpuobj->im_backing)
- return -EINVAL;
-
return 0;
}
void
nv04_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- if (gpuobj && gpuobj->im_backing) {
- if (gpuobj->im_bound)
- dev_priv->engine.instmem.unbind(dev, gpuobj);
- gpuobj->im_backing = NULL;
- }
}
int
nv04_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
- if (!gpuobj->im_pramin || gpuobj->im_bound)
- return -EINVAL;
-
- gpuobj->im_bound = 1;
return 0;
}
int
nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
- if (gpuobj->im_bound == 0)
- return -EINVAL;
-
- gpuobj->im_bound = 0;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c
new file mode 100644
index 000000000000..6a6eb697d38e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_pm.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+#include "nouveau_pm.h"
+
+struct nv04_pm_state {
+ struct pll_lims pll;
+ struct nouveau_pll_vals calc;
+};
+
+int
+nv04_pm_clock_get(struct drm_device *dev, u32 id)
+{
+ return nouveau_hw_get_clock(dev, id);
+}
+
+void *
+nv04_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
+ u32 id, int khz)
+{
+ struct nv04_pm_state *state;
+ int ret;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ ret = get_pll_limits(dev, id, &state->pll);
+ if (ret) {
+ kfree(state);
+ return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
+ }
+
+ ret = nouveau_calc_pll_mnp(dev, &state->pll, khz, &state->calc);
+ if (!ret) {
+ kfree(state);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return state;
+}
+
+void
+nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_pm_state *state = pre_state;
+ u32 reg = state->pll.reg;
+
+ /* thank the insane nouveau_hw_setpll() interface for this */
+ if (dev_priv->card_type >= NV_40)
+ reg += 4;
+
+ nouveau_hw_setpll(dev, reg, &state->calc);
+ kfree(state);
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
index 0b5d012d7c28..3eb605ddfd03 100644
--- a/drivers/gpu/drm/nouveau/nv04_tv.c
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
@@ -49,8 +49,8 @@ static struct i2c_board_info nv04_tv_encoder_info[] = {
int nv04_tv_identify(struct drm_device *dev, int i2c_index)
{
- return nouveau_i2c_identify(dev, "TV encoder",
- nv04_tv_encoder_info, i2c_index);
+ return nouveau_i2c_identify(dev, "TV encoder", nv04_tv_encoder_info,
+ NULL, i2c_index);
}
@@ -99,12 +99,10 @@ static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
state->tv_setup = 0;
- if (bind) {
- state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
+ if (bind)
state->CRTC[NV_CIO_CRE_49] |= 0x10;
- } else {
+ else
state->CRTC[NV_CIO_CRE_49] &= ~0x10;
- }
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
state->CRTC[NV_CIO_CRE_LCD__INDEX]);
diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c
index 7a4069cf5d0b..f1b03ad58fd5 100644
--- a/drivers/gpu/drm/nouveau/nv10_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv10_fifo.c
@@ -27,8 +27,9 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
-#define NV10_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV10_RAMFC__SIZE))
+#define NV10_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV10_RAMFC__SIZE))
#define NV10_RAMFC__SIZE ((dev_priv->chipset) >= 0x17 ? 64 : 32)
int
@@ -48,7 +49,7 @@ nv10_fifo_create_context(struct nouveau_channel *chan)
ret = nouveau_gpuobj_new_fake(dev, NV10_RAMFC(chan->id), ~0,
NV10_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
- NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
+ NVOBJ_FLAG_ZERO_FREE, &chan->ramfc);
if (ret)
return ret;
@@ -57,7 +58,7 @@ nv10_fifo_create_context(struct nouveau_channel *chan)
*/
nv_wi32(dev, fc + 0, chan->pushbuf_base);
nv_wi32(dev, fc + 4, chan->pushbuf_base);
- nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
+ nv_wi32(dev, fc + 12, chan->pushbuf->pinst >> 4);
nv_wi32(dev, fc + 20, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
@@ -80,7 +81,7 @@ nv10_fifo_destroy_context(struct nouveau_channel *chan)
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
- nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+ nouveau_gpuobj_ref(NULL, &chan->ramfc);
}
static void
@@ -202,14 +203,14 @@ nv10_fifo_init_ramxx(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
- ((dev_priv->ramht_bits - 9) << 16) |
- (dev_priv->ramht_offset >> 8));
- nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
+ ((dev_priv->ramht->bits - 9) << 16) |
+ (dev_priv->ramht->gpuobj->pinst >> 8));
+ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8);
if (dev_priv->chipset < 0x17) {
- nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
+ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc->pinst >> 8);
} else {
- nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc_offset >> 8) |
+ nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc->pinst >> 8) |
(1 << 16) /* 64 Bytes entry*/);
/* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */
}
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
index b2f6a57c0cc5..8e68c9731159 100644
--- a/drivers/gpu/drm/nouveau/nv10_graph.c
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
@@ -803,7 +803,7 @@ nv10_graph_context_switch(struct drm_device *dev)
/* Load context for next channel */
chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
chan = dev_priv->fifos[chid];
- if (chan)
+ if (chan && chan->pgraph_ctx)
nv10_graph_load_context(chan);
pgraph->fifo_access(dev, true);
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
index 13cdc05b7c2d..28119fd19d03 100644
--- a/drivers/gpu/drm/nouveau/nv17_tv.c
+++ b/drivers/gpu/drm/nouveau/nv17_tv.c
@@ -193,55 +193,56 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
}
}
-static const struct {
- int hdisplay;
- int vdisplay;
-} modes[] = {
- { 640, 400 },
- { 640, 480 },
- { 720, 480 },
- { 720, 576 },
- { 800, 600 },
- { 1024, 768 },
- { 1280, 720 },
- { 1280, 1024 },
- { 1920, 1080 }
-};
-
-static int nv17_tv_get_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
+static int nv17_tv_get_ld_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
{
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
- struct drm_display_mode *mode;
- struct drm_display_mode *output_mode;
+ struct drm_display_mode *mode, *tv_mode;
int n = 0;
- int i;
-
- if (tv_norm->kind != CTV_ENC_MODE) {
- struct drm_display_mode *tv_mode;
- for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
- mode = drm_mode_duplicate(encoder->dev, tv_mode);
+ for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
+ mode = drm_mode_duplicate(encoder->dev, tv_mode);
- mode->clock = tv_norm->tv_enc_mode.vrefresh *
- mode->htotal / 1000 *
- mode->vtotal / 1000;
+ mode->clock = tv_norm->tv_enc_mode.vrefresh *
+ mode->htotal / 1000 *
+ mode->vtotal / 1000;
- if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
- mode->clock *= 2;
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ mode->clock *= 2;
- if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
- mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
- mode->type |= DRM_MODE_TYPE_PREFERRED;
+ if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
+ mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
- drm_mode_probed_add(connector, mode);
- n++;
- }
- return n;
+ drm_mode_probed_add(connector, mode);
+ n++;
}
- /* tv_norm->kind == CTV_ENC_MODE */
- output_mode = &tv_norm->ctv_enc_mode.mode;
+ return n;
+}
+
+static int nv17_tv_get_hd_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+ struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode;
+ struct drm_display_mode *mode;
+ const struct {
+ int hdisplay;
+ int vdisplay;
+ } modes[] = {
+ { 640, 400 },
+ { 640, 480 },
+ { 720, 480 },
+ { 720, 576 },
+ { 800, 600 },
+ { 1024, 768 },
+ { 1280, 720 },
+ { 1280, 1024 },
+ { 1920, 1080 }
+ };
+ int i, n = 0;
+
for (i = 0; i < ARRAY_SIZE(modes); i++) {
if (modes[i].hdisplay > output_mode->hdisplay ||
modes[i].vdisplay > output_mode->vdisplay)
@@ -251,11 +252,12 @@ static int nv17_tv_get_modes(struct drm_encoder *encoder,
modes[i].vdisplay == output_mode->vdisplay) {
mode = drm_mode_duplicate(encoder->dev, output_mode);
mode->type |= DRM_MODE_TYPE_PREFERRED;
+
} else {
mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
- modes[i].vdisplay, 60, false,
- output_mode->flags & DRM_MODE_FLAG_INTERLACE,
- false);
+ modes[i].vdisplay, 60, false,
+ (output_mode->flags &
+ DRM_MODE_FLAG_INTERLACE), false);
}
/* CVT modes are sometimes unsuitable... */
@@ -266,6 +268,7 @@ static int nv17_tv_get_modes(struct drm_encoder *encoder,
- mode->hdisplay) * 9 / 10) & ~7;
mode->hsync_end = mode->hsync_start + 8;
}
+
if (output_mode->vdisplay >= 1024) {
mode->vtotal = output_mode->vtotal;
mode->vsync_start = output_mode->vsync_start;
@@ -276,9 +279,21 @@ static int nv17_tv_get_modes(struct drm_encoder *encoder,
drm_mode_probed_add(connector, mode);
n++;
}
+
return n;
}
+static int nv17_tv_get_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+ if (tv_norm->kind == CTV_ENC_MODE)
+ return nv17_tv_get_hd_modes(encoder, connector);
+ else
+ return nv17_tv_get_ld_modes(encoder, connector);
+}
+
static int nv17_tv_mode_valid(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
@@ -408,15 +423,8 @@ static void nv17_tv_prepare(struct drm_encoder *encoder)
}
- /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
- * at LCD__INDEX which we don't alter
- */
- if (!(*cr_lcd & 0x44)) {
- if (tv_norm->kind == CTV_ENC_MODE)
- *cr_lcd = 0x1 | (head ? 0x0 : 0x8);
- else
- *cr_lcd = 0;
- }
+ if (tv_norm->kind == CTV_ENC_MODE)
+ *cr_lcd |= 0x1 | (head ? 0x0 : 0x8);
/* Set the DACCLK register */
dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h
index c00977cedabd..6bf03840f9eb 100644
--- a/drivers/gpu/drm/nouveau/nv17_tv.h
+++ b/drivers/gpu/drm/nouveau/nv17_tv.h
@@ -127,7 +127,8 @@ void nv17_ctv_update_rescaler(struct drm_encoder *encoder);
/* TV hardware access functions */
-static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t val)
+static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg,
+ uint32_t val)
{
nv_wr32(dev, reg, val);
}
@@ -137,7 +138,8 @@ static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg)
return nv_rd32(dev, reg);
}
-static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, uint8_t val)
+static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg,
+ uint8_t val)
{
nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
nv_write_ptv(dev, NV_PTV_TV_DATA, val);
@@ -149,8 +151,11 @@ static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg)
return nv_read_ptv(dev, NV_PTV_TV_DATA);
}
-#define nv_load_ptv(dev, state, reg) nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg)
-#define nv_save_ptv(dev, state, reg) state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg)
-#define nv_load_tv_enc(dev, state, reg) nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg])
+#define nv_load_ptv(dev, state, reg) \
+ nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg)
+#define nv_save_ptv(dev, state, reg) \
+ state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg)
+#define nv_load_tv_enc(dev, state, reg) \
+ nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg])
#endif
diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
index d64683d97e0d..9d3893c50a41 100644
--- a/drivers/gpu/drm/nouveau/nv17_tv_modes.c
+++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
@@ -336,12 +336,17 @@ static void tv_setup_filter(struct drm_encoder *encoder)
struct filter_params *p = &fparams[k][j];
for (i = 0; i < 7; i++) {
- int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + p->ki3*i*i*i)
- + (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k]
- + (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker
- + (p->krf + p->kirf*i + p->ki2rf*i*i + p->ki3rf*i*i*i)*flicker*rs[k];
-
- (*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f << 9);
+ int64_t c = (p->k1 + p->ki*i + p->ki2*i*i +
+ p->ki3*i*i*i)
+ + (p->kr + p->kir*i + p->ki2r*i*i +
+ p->ki3r*i*i*i) * rs[k]
+ + (p->kf + p->kif*i + p->ki2f*i*i +
+ p->ki3f*i*i*i) * flicker
+ + (p->krf + p->kirf*i + p->ki2rf*i*i +
+ p->ki3rf*i*i*i) * flicker * rs[k];
+
+ (*filters[k])[j][i] = (c + id5/2) >> 39
+ & (0x1 << 31 | 0x7f << 9);
}
}
}
@@ -349,7 +354,8 @@ static void tv_setup_filter(struct drm_encoder *encoder)
/* Hardware state saving/restoring */
-static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
+static void tv_save_filter(struct drm_device *dev, uint32_t base,
+ uint32_t regs[4][7])
{
int i, j;
uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
@@ -360,7 +366,8 @@ static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[
}
}
-static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
+static void tv_load_filter(struct drm_device *dev, uint32_t base,
+ uint32_t regs[4][7])
{
int i, j;
uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
@@ -504,10 +511,10 @@ void nv17_tv_update_properties(struct drm_encoder *encoder)
break;
}
- regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], 255,
- tv_enc->saturation);
- regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], 255,
- tv_enc->saturation);
+ regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20],
+ 255, tv_enc->saturation);
+ regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22],
+ 255, tv_enc->saturation);
regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
nv_load_ptv(dev, regs, 204);
@@ -541,7 +548,8 @@ void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
int head = nouveau_crtc(encoder->crtc)->index;
struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
- struct drm_display_mode *output_mode = &get_tv_norm(encoder)->ctv_enc_mode.mode;
+ struct drm_display_mode *output_mode =
+ &get_tv_norm(encoder)->ctv_enc_mode.mode;
int overscan, hmargin, vmargin, hratio, vratio;
/* The rescaler doesn't do the right thing for interlaced modes. */
@@ -553,13 +561,15 @@ void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
- hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin,
- overscan);
- vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin,
- overscan);
+ hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20),
+ hmargin, overscan);
+ vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20),
+ vmargin, overscan);
- hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay - 2*hmargin);
- vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay - 2*vmargin) & ~3;
+ hratio = crtc_mode->hdisplay * 0x800 /
+ (output_mode->hdisplay - 2*hmargin);
+ vratio = crtc_mode->vdisplay * 0x800 /
+ (output_mode->vdisplay - 2*vmargin) & ~3;
regs->fp_horiz_regs[FP_VALID_START] = hmargin;
regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
index 17f309b36c91..12ab9cd56eca 100644
--- a/drivers/gpu/drm/nouveau/nv20_graph.c
+++ b/drivers/gpu/drm/nouveau/nv20_graph.c
@@ -37,49 +37,49 @@ nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
- nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
- nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
- nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
- nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
+ nv_wo32(ctx, 0x033c, 0xffff0000);
+ nv_wo32(ctx, 0x03a0, 0x0fff0000);
+ nv_wo32(ctx, 0x03a4, 0x0fff0000);
+ nv_wo32(ctx, 0x047c, 0x00000101);
+ nv_wo32(ctx, 0x0490, 0x00000111);
+ nv_wo32(ctx, 0x04a8, 0x44400000);
for (i = 0x04d4; i <= 0x04e0; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00030303);
+ nv_wo32(ctx, i, 0x00030303);
for (i = 0x04f4; i <= 0x0500; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080000);
+ nv_wo32(ctx, i, 0x00080000);
for (i = 0x050c; i <= 0x0518; i += 4)
- nv_wo32(dev, ctx, i/4, 0x01012000);
+ nv_wo32(ctx, i, 0x01012000);
for (i = 0x051c; i <= 0x0528; i += 4)
- nv_wo32(dev, ctx, i/4, 0x000105b8);
+ nv_wo32(ctx, i, 0x000105b8);
for (i = 0x052c; i <= 0x0538; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080008);
+ nv_wo32(ctx, i, 0x00080008);
for (i = 0x055c; i <= 0x0598; i += 4)
- nv_wo32(dev, ctx, i/4, 0x07ff0000);
- nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
- nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
- nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
- nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
- nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
- nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
+ nv_wo32(ctx, i, 0x07ff0000);
+ nv_wo32(ctx, 0x05a4, 0x4b7fffff);
+ nv_wo32(ctx, 0x05fc, 0x00000001);
+ nv_wo32(ctx, 0x0604, 0x00004000);
+ nv_wo32(ctx, 0x0610, 0x00000001);
+ nv_wo32(ctx, 0x0618, 0x00040000);
+ nv_wo32(ctx, 0x061c, 0x00010000);
for (i = 0x1c1c; i <= 0x248c; i += 16) {
- nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
- nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
- nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
+ nv_wo32(ctx, (i + 0), 0x10700ff9);
+ nv_wo32(ctx, (i + 4), 0x0436086c);
+ nv_wo32(ctx, (i + 8), 0x000c001b);
}
- nv_wo32(dev, ctx, 0x281c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2830/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x285c/4, 0x40000000);
- nv_wo32(dev, ctx, 0x2860/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2864/4, 0x3f000000);
- nv_wo32(dev, ctx, 0x286c/4, 0x40000000);
- nv_wo32(dev, ctx, 0x2870/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2878/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x2880/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x34a4/4, 0x000fe000);
- nv_wo32(dev, ctx, 0x3530/4, 0x000003f8);
- nv_wo32(dev, ctx, 0x3540/4, 0x002fe000);
+ nv_wo32(ctx, 0x281c, 0x3f800000);
+ nv_wo32(ctx, 0x2830, 0x3f800000);
+ nv_wo32(ctx, 0x285c, 0x40000000);
+ nv_wo32(ctx, 0x2860, 0x3f800000);
+ nv_wo32(ctx, 0x2864, 0x3f000000);
+ nv_wo32(ctx, 0x286c, 0x40000000);
+ nv_wo32(ctx, 0x2870, 0x3f800000);
+ nv_wo32(ctx, 0x2878, 0xbf800000);
+ nv_wo32(ctx, 0x2880, 0xbf800000);
+ nv_wo32(ctx, 0x34a4, 0x000fe000);
+ nv_wo32(ctx, 0x3530, 0x000003f8);
+ nv_wo32(ctx, 0x3540, 0x002fe000);
for (i = 0x355c; i <= 0x3578; i += 4)
- nv_wo32(dev, ctx, i/4, 0x001c527c);
+ nv_wo32(ctx, i, 0x001c527c);
}
static void
@@ -87,58 +87,58 @@ nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
- nv_wo32(dev, ctx, 0x035c/4, 0xffff0000);
- nv_wo32(dev, ctx, 0x03c0/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x03c4/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x049c/4, 0x00000101);
- nv_wo32(dev, ctx, 0x04b0/4, 0x00000111);
- nv_wo32(dev, ctx, 0x04c8/4, 0x00000080);
- nv_wo32(dev, ctx, 0x04cc/4, 0xffff0000);
- nv_wo32(dev, ctx, 0x04d0/4, 0x00000001);
- nv_wo32(dev, ctx, 0x04e4/4, 0x44400000);
- nv_wo32(dev, ctx, 0x04fc/4, 0x4b800000);
+ nv_wo32(ctx, 0x035c, 0xffff0000);
+ nv_wo32(ctx, 0x03c0, 0x0fff0000);
+ nv_wo32(ctx, 0x03c4, 0x0fff0000);
+ nv_wo32(ctx, 0x049c, 0x00000101);
+ nv_wo32(ctx, 0x04b0, 0x00000111);
+ nv_wo32(ctx, 0x04c8, 0x00000080);
+ nv_wo32(ctx, 0x04cc, 0xffff0000);
+ nv_wo32(ctx, 0x04d0, 0x00000001);
+ nv_wo32(ctx, 0x04e4, 0x44400000);
+ nv_wo32(ctx, 0x04fc, 0x4b800000);
for (i = 0x0510; i <= 0x051c; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00030303);
+ nv_wo32(ctx, i, 0x00030303);
for (i = 0x0530; i <= 0x053c; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080000);
+ nv_wo32(ctx, i, 0x00080000);
for (i = 0x0548; i <= 0x0554; i += 4)
- nv_wo32(dev, ctx, i/4, 0x01012000);
+ nv_wo32(ctx, i, 0x01012000);
for (i = 0x0558; i <= 0x0564; i += 4)
- nv_wo32(dev, ctx, i/4, 0x000105b8);
+ nv_wo32(ctx, i, 0x000105b8);
for (i = 0x0568; i <= 0x0574; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080008);
+ nv_wo32(ctx, i, 0x00080008);
for (i = 0x0598; i <= 0x05d4; i += 4)
- nv_wo32(dev, ctx, i/4, 0x07ff0000);
- nv_wo32(dev, ctx, 0x05e0/4, 0x4b7fffff);
- nv_wo32(dev, ctx, 0x0620/4, 0x00000080);
- nv_wo32(dev, ctx, 0x0624/4, 0x30201000);
- nv_wo32(dev, ctx, 0x0628/4, 0x70605040);
- nv_wo32(dev, ctx, 0x062c/4, 0xb0a09080);
- nv_wo32(dev, ctx, 0x0630/4, 0xf0e0d0c0);
- nv_wo32(dev, ctx, 0x0664/4, 0x00000001);
- nv_wo32(dev, ctx, 0x066c/4, 0x00004000);
- nv_wo32(dev, ctx, 0x0678/4, 0x00000001);
- nv_wo32(dev, ctx, 0x0680/4, 0x00040000);
- nv_wo32(dev, ctx, 0x0684/4, 0x00010000);
+ nv_wo32(ctx, i, 0x07ff0000);
+ nv_wo32(ctx, 0x05e0, 0x4b7fffff);
+ nv_wo32(ctx, 0x0620, 0x00000080);
+ nv_wo32(ctx, 0x0624, 0x30201000);
+ nv_wo32(ctx, 0x0628, 0x70605040);
+ nv_wo32(ctx, 0x062c, 0xb0a09080);
+ nv_wo32(ctx, 0x0630, 0xf0e0d0c0);
+ nv_wo32(ctx, 0x0664, 0x00000001);
+ nv_wo32(ctx, 0x066c, 0x00004000);
+ nv_wo32(ctx, 0x0678, 0x00000001);
+ nv_wo32(ctx, 0x0680, 0x00040000);
+ nv_wo32(ctx, 0x0684, 0x00010000);
for (i = 0x1b04; i <= 0x2374; i += 16) {
- nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
- nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
- nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
+ nv_wo32(ctx, (i + 0), 0x10700ff9);
+ nv_wo32(ctx, (i + 4), 0x0436086c);
+ nv_wo32(ctx, (i + 8), 0x000c001b);
}
- nv_wo32(dev, ctx, 0x2704/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2718/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2744/4, 0x40000000);
- nv_wo32(dev, ctx, 0x2748/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x274c/4, 0x3f000000);
- nv_wo32(dev, ctx, 0x2754/4, 0x40000000);
- nv_wo32(dev, ctx, 0x2758/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2760/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x2768/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x308c/4, 0x000fe000);
- nv_wo32(dev, ctx, 0x3108/4, 0x000003f8);
- nv_wo32(dev, ctx, 0x3468/4, 0x002fe000);
+ nv_wo32(ctx, 0x2704, 0x3f800000);
+ nv_wo32(ctx, 0x2718, 0x3f800000);
+ nv_wo32(ctx, 0x2744, 0x40000000);
+ nv_wo32(ctx, 0x2748, 0x3f800000);
+ nv_wo32(ctx, 0x274c, 0x3f000000);
+ nv_wo32(ctx, 0x2754, 0x40000000);
+ nv_wo32(ctx, 0x2758, 0x3f800000);
+ nv_wo32(ctx, 0x2760, 0xbf800000);
+ nv_wo32(ctx, 0x2768, 0xbf800000);
+ nv_wo32(ctx, 0x308c, 0x000fe000);
+ nv_wo32(ctx, 0x3108, 0x000003f8);
+ nv_wo32(ctx, 0x3468, 0x002fe000);
for (i = 0x3484; i <= 0x34a0; i += 4)
- nv_wo32(dev, ctx, i/4, 0x001c527c);
+ nv_wo32(ctx, i, 0x001c527c);
}
static void
@@ -146,49 +146,49 @@ nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
- nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
- nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
- nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
- nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
+ nv_wo32(ctx, 0x033c, 0xffff0000);
+ nv_wo32(ctx, 0x03a0, 0x0fff0000);
+ nv_wo32(ctx, 0x03a4, 0x0fff0000);
+ nv_wo32(ctx, 0x047c, 0x00000101);
+ nv_wo32(ctx, 0x0490, 0x00000111);
+ nv_wo32(ctx, 0x04a8, 0x44400000);
for (i = 0x04d4; i <= 0x04e0; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00030303);
+ nv_wo32(ctx, i, 0x00030303);
for (i = 0x04f4; i <= 0x0500; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080000);
+ nv_wo32(ctx, i, 0x00080000);
for (i = 0x050c; i <= 0x0518; i += 4)
- nv_wo32(dev, ctx, i/4, 0x01012000);
+ nv_wo32(ctx, i, 0x01012000);
for (i = 0x051c; i <= 0x0528; i += 4)
- nv_wo32(dev, ctx, i/4, 0x000105b8);
+ nv_wo32(ctx, i, 0x000105b8);
for (i = 0x052c; i <= 0x0538; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080008);
+ nv_wo32(ctx, i, 0x00080008);
for (i = 0x055c; i <= 0x0598; i += 4)
- nv_wo32(dev, ctx, i/4, 0x07ff0000);
- nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
- nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
- nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
- nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
- nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
- nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
+ nv_wo32(ctx, i, 0x07ff0000);
+ nv_wo32(ctx, 0x05a4, 0x4b7fffff);
+ nv_wo32(ctx, 0x05fc, 0x00000001);
+ nv_wo32(ctx, 0x0604, 0x00004000);
+ nv_wo32(ctx, 0x0610, 0x00000001);
+ nv_wo32(ctx, 0x0618, 0x00040000);
+ nv_wo32(ctx, 0x061c, 0x00010000);
for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */
- nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
- nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
- nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
+ nv_wo32(ctx, (i + 0), 0x10700ff9);
+ nv_wo32(ctx, (i + 4), 0x0436086c);
+ nv_wo32(ctx, (i + 8), 0x000c001b);
}
- nv_wo32(dev, ctx, 0x269c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x26b0/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x26dc/4, 0x40000000);
- nv_wo32(dev, ctx, 0x26e0/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x26e4/4, 0x3f000000);
- nv_wo32(dev, ctx, 0x26ec/4, 0x40000000);
- nv_wo32(dev, ctx, 0x26f0/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x26f8/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x2700/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x3024/4, 0x000fe000);
- nv_wo32(dev, ctx, 0x30a0/4, 0x000003f8);
- nv_wo32(dev, ctx, 0x33fc/4, 0x002fe000);
+ nv_wo32(ctx, 0x269c, 0x3f800000);
+ nv_wo32(ctx, 0x26b0, 0x3f800000);
+ nv_wo32(ctx, 0x26dc, 0x40000000);
+ nv_wo32(ctx, 0x26e0, 0x3f800000);
+ nv_wo32(ctx, 0x26e4, 0x3f000000);
+ nv_wo32(ctx, 0x26ec, 0x40000000);
+ nv_wo32(ctx, 0x26f0, 0x3f800000);
+ nv_wo32(ctx, 0x26f8, 0xbf800000);
+ nv_wo32(ctx, 0x2700, 0xbf800000);
+ nv_wo32(ctx, 0x3024, 0x000fe000);
+ nv_wo32(ctx, 0x30a0, 0x000003f8);
+ nv_wo32(ctx, 0x33fc, 0x002fe000);
for (i = 0x341c; i <= 0x3438; i += 4)
- nv_wo32(dev, ctx, i/4, 0x001c527c);
+ nv_wo32(ctx, i, 0x001c527c);
}
static void
@@ -196,57 +196,57 @@ nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
- nv_wo32(dev, ctx, 0x0410/4, 0x00000101);
- nv_wo32(dev, ctx, 0x0424/4, 0x00000111);
- nv_wo32(dev, ctx, 0x0428/4, 0x00000060);
- nv_wo32(dev, ctx, 0x0444/4, 0x00000080);
- nv_wo32(dev, ctx, 0x0448/4, 0xffff0000);
- nv_wo32(dev, ctx, 0x044c/4, 0x00000001);
- nv_wo32(dev, ctx, 0x0460/4, 0x44400000);
- nv_wo32(dev, ctx, 0x048c/4, 0xffff0000);
+ nv_wo32(ctx, 0x0410, 0x00000101);
+ nv_wo32(ctx, 0x0424, 0x00000111);
+ nv_wo32(ctx, 0x0428, 0x00000060);
+ nv_wo32(ctx, 0x0444, 0x00000080);
+ nv_wo32(ctx, 0x0448, 0xffff0000);
+ nv_wo32(ctx, 0x044c, 0x00000001);
+ nv_wo32(ctx, 0x0460, 0x44400000);
+ nv_wo32(ctx, 0x048c, 0xffff0000);
for (i = 0x04e0; i < 0x04e8; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x04ec/4, 0x00011100);
+ nv_wo32(ctx, i, 0x0fff0000);
+ nv_wo32(ctx, 0x04ec, 0x00011100);
for (i = 0x0508; i < 0x0548; i += 4)
- nv_wo32(dev, ctx, i/4, 0x07ff0000);
- nv_wo32(dev, ctx, 0x0550/4, 0x4b7fffff);
- nv_wo32(dev, ctx, 0x058c/4, 0x00000080);
- nv_wo32(dev, ctx, 0x0590/4, 0x30201000);
- nv_wo32(dev, ctx, 0x0594/4, 0x70605040);
- nv_wo32(dev, ctx, 0x0598/4, 0xb8a89888);
- nv_wo32(dev, ctx, 0x059c/4, 0xf8e8d8c8);
- nv_wo32(dev, ctx, 0x05b0/4, 0xb0000000);
+ nv_wo32(ctx, i, 0x07ff0000);
+ nv_wo32(ctx, 0x0550, 0x4b7fffff);
+ nv_wo32(ctx, 0x058c, 0x00000080);
+ nv_wo32(ctx, 0x0590, 0x30201000);
+ nv_wo32(ctx, 0x0594, 0x70605040);
+ nv_wo32(ctx, 0x0598, 0xb8a89888);
+ nv_wo32(ctx, 0x059c, 0xf8e8d8c8);
+ nv_wo32(ctx, 0x05b0, 0xb0000000);
for (i = 0x0600; i < 0x0640; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00010588);
+ nv_wo32(ctx, i, 0x00010588);
for (i = 0x0640; i < 0x0680; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00030303);
+ nv_wo32(ctx, i, 0x00030303);
for (i = 0x06c0; i < 0x0700; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0008aae4);
+ nv_wo32(ctx, i, 0x0008aae4);
for (i = 0x0700; i < 0x0740; i += 4)
- nv_wo32(dev, ctx, i/4, 0x01012000);
+ nv_wo32(ctx, i, 0x01012000);
for (i = 0x0740; i < 0x0780; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080008);
- nv_wo32(dev, ctx, 0x085c/4, 0x00040000);
- nv_wo32(dev, ctx, 0x0860/4, 0x00010000);
+ nv_wo32(ctx, i, 0x00080008);
+ nv_wo32(ctx, 0x085c, 0x00040000);
+ nv_wo32(ctx, 0x0860, 0x00010000);
for (i = 0x0864; i < 0x0874; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00040004);
+ nv_wo32(ctx, i, 0x00040004);
for (i = 0x1f18; i <= 0x3088 ; i += 16) {
- nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
- nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
- nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
+ nv_wo32(ctx, i + 0, 0x10700ff9);
+ nv_wo32(ctx, i + 1, 0x0436086c);
+ nv_wo32(ctx, i + 2, 0x000c001b);
}
for (i = 0x30b8; i < 0x30c8; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0000ffff);
- nv_wo32(dev, ctx, 0x344c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x3808/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x381c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x3848/4, 0x40000000);
- nv_wo32(dev, ctx, 0x384c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x3850/4, 0x3f000000);
- nv_wo32(dev, ctx, 0x3858/4, 0x40000000);
- nv_wo32(dev, ctx, 0x385c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x3864/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x386c/4, 0xbf800000);
+ nv_wo32(ctx, i, 0x0000ffff);
+ nv_wo32(ctx, 0x344c, 0x3f800000);
+ nv_wo32(ctx, 0x3808, 0x3f800000);
+ nv_wo32(ctx, 0x381c, 0x3f800000);
+ nv_wo32(ctx, 0x3848, 0x40000000);
+ nv_wo32(ctx, 0x384c, 0x3f800000);
+ nv_wo32(ctx, 0x3850, 0x3f000000);
+ nv_wo32(ctx, 0x3858, 0x40000000);
+ nv_wo32(ctx, 0x385c, 0x3f800000);
+ nv_wo32(ctx, 0x3864, 0xbf800000);
+ nv_wo32(ctx, 0x386c, 0xbf800000);
}
static void
@@ -254,57 +254,57 @@ nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
- nv_wo32(dev, ctx, 0x040c/4, 0x01000101);
- nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
- nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
- nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
- nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
- nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
- nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
- nv_wo32(dev, ctx, 0x0480/4, 0xffff0000);
+ nv_wo32(ctx, 0x040c, 0x01000101);
+ nv_wo32(ctx, 0x0420, 0x00000111);
+ nv_wo32(ctx, 0x0424, 0x00000060);
+ nv_wo32(ctx, 0x0440, 0x00000080);
+ nv_wo32(ctx, 0x0444, 0xffff0000);
+ nv_wo32(ctx, 0x0448, 0x00000001);
+ nv_wo32(ctx, 0x045c, 0x44400000);
+ nv_wo32(ctx, 0x0480, 0xffff0000);
for (i = 0x04d4; i < 0x04dc; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x04e0/4, 0x00011100);
+ nv_wo32(ctx, i, 0x0fff0000);
+ nv_wo32(ctx, 0x04e0, 0x00011100);
for (i = 0x04fc; i < 0x053c; i += 4)
- nv_wo32(dev, ctx, i/4, 0x07ff0000);
- nv_wo32(dev, ctx, 0x0544/4, 0x4b7fffff);
- nv_wo32(dev, ctx, 0x057c/4, 0x00000080);
- nv_wo32(dev, ctx, 0x0580/4, 0x30201000);
- nv_wo32(dev, ctx, 0x0584/4, 0x70605040);
- nv_wo32(dev, ctx, 0x0588/4, 0xb8a89888);
- nv_wo32(dev, ctx, 0x058c/4, 0xf8e8d8c8);
- nv_wo32(dev, ctx, 0x05a0/4, 0xb0000000);
+ nv_wo32(ctx, i, 0x07ff0000);
+ nv_wo32(ctx, 0x0544, 0x4b7fffff);
+ nv_wo32(ctx, 0x057c, 0x00000080);
+ nv_wo32(ctx, 0x0580, 0x30201000);
+ nv_wo32(ctx, 0x0584, 0x70605040);
+ nv_wo32(ctx, 0x0588, 0xb8a89888);
+ nv_wo32(ctx, 0x058c, 0xf8e8d8c8);
+ nv_wo32(ctx, 0x05a0, 0xb0000000);
for (i = 0x05f0; i < 0x0630; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00010588);
+ nv_wo32(ctx, i, 0x00010588);
for (i = 0x0630; i < 0x0670; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00030303);
+ nv_wo32(ctx, i, 0x00030303);
for (i = 0x06b0; i < 0x06f0; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0008aae4);
+ nv_wo32(ctx, i, 0x0008aae4);
for (i = 0x06f0; i < 0x0730; i += 4)
- nv_wo32(dev, ctx, i/4, 0x01012000);
+ nv_wo32(ctx, i, 0x01012000);
for (i = 0x0730; i < 0x0770; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080008);
- nv_wo32(dev, ctx, 0x0850/4, 0x00040000);
- nv_wo32(dev, ctx, 0x0854/4, 0x00010000);
+ nv_wo32(ctx, i, 0x00080008);
+ nv_wo32(ctx, 0x0850, 0x00040000);
+ nv_wo32(ctx, 0x0854, 0x00010000);
for (i = 0x0858; i < 0x0868; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00040004);
+ nv_wo32(ctx, i, 0x00040004);
for (i = 0x15ac; i <= 0x271c ; i += 16) {
- nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
- nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
- nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
+ nv_wo32(ctx, i + 0, 0x10700ff9);
+ nv_wo32(ctx, i + 1, 0x0436086c);
+ nv_wo32(ctx, i + 2, 0x000c001b);
}
for (i = 0x274c; i < 0x275c; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0000ffff);
- nv_wo32(dev, ctx, 0x2ae0/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2e9c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2eb0/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2edc/4, 0x40000000);
- nv_wo32(dev, ctx, 0x2ee0/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2ee4/4, 0x3f000000);
- nv_wo32(dev, ctx, 0x2eec/4, 0x40000000);
- nv_wo32(dev, ctx, 0x2ef0/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x2ef8/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x2f00/4, 0xbf800000);
+ nv_wo32(ctx, i, 0x0000ffff);
+ nv_wo32(ctx, 0x2ae0, 0x3f800000);
+ nv_wo32(ctx, 0x2e9c, 0x3f800000);
+ nv_wo32(ctx, 0x2eb0, 0x3f800000);
+ nv_wo32(ctx, 0x2edc, 0x40000000);
+ nv_wo32(ctx, 0x2ee0, 0x3f800000);
+ nv_wo32(ctx, 0x2ee4, 0x3f000000);
+ nv_wo32(ctx, 0x2eec, 0x40000000);
+ nv_wo32(ctx, 0x2ef0, 0x3f800000);
+ nv_wo32(ctx, 0x2ef8, 0xbf800000);
+ nv_wo32(ctx, 0x2f00, 0xbf800000);
}
static void
@@ -312,57 +312,57 @@ nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
- nv_wo32(dev, ctx, 0x040c/4, 0x00000101);
- nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
- nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
- nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
- nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
- nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
- nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
- nv_wo32(dev, ctx, 0x0488/4, 0xffff0000);
+ nv_wo32(ctx, 0x040c, 0x00000101);
+ nv_wo32(ctx, 0x0420, 0x00000111);
+ nv_wo32(ctx, 0x0424, 0x00000060);
+ nv_wo32(ctx, 0x0440, 0x00000080);
+ nv_wo32(ctx, 0x0444, 0xffff0000);
+ nv_wo32(ctx, 0x0448, 0x00000001);
+ nv_wo32(ctx, 0x045c, 0x44400000);
+ nv_wo32(ctx, 0x0488, 0xffff0000);
for (i = 0x04dc; i < 0x04e4; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0fff0000);
- nv_wo32(dev, ctx, 0x04e8/4, 0x00011100);
+ nv_wo32(ctx, i, 0x0fff0000);
+ nv_wo32(ctx, 0x04e8, 0x00011100);
for (i = 0x0504; i < 0x0544; i += 4)
- nv_wo32(dev, ctx, i/4, 0x07ff0000);
- nv_wo32(dev, ctx, 0x054c/4, 0x4b7fffff);
- nv_wo32(dev, ctx, 0x0588/4, 0x00000080);
- nv_wo32(dev, ctx, 0x058c/4, 0x30201000);
- nv_wo32(dev, ctx, 0x0590/4, 0x70605040);
- nv_wo32(dev, ctx, 0x0594/4, 0xb8a89888);
- nv_wo32(dev, ctx, 0x0598/4, 0xf8e8d8c8);
- nv_wo32(dev, ctx, 0x05ac/4, 0xb0000000);
+ nv_wo32(ctx, i, 0x07ff0000);
+ nv_wo32(ctx, 0x054c, 0x4b7fffff);
+ nv_wo32(ctx, 0x0588, 0x00000080);
+ nv_wo32(ctx, 0x058c, 0x30201000);
+ nv_wo32(ctx, 0x0590, 0x70605040);
+ nv_wo32(ctx, 0x0594, 0xb8a89888);
+ nv_wo32(ctx, 0x0598, 0xf8e8d8c8);
+ nv_wo32(ctx, 0x05ac, 0xb0000000);
for (i = 0x0604; i < 0x0644; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00010588);
+ nv_wo32(ctx, i, 0x00010588);
for (i = 0x0644; i < 0x0684; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00030303);
+ nv_wo32(ctx, i, 0x00030303);
for (i = 0x06c4; i < 0x0704; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0008aae4);
+ nv_wo32(ctx, i, 0x0008aae4);
for (i = 0x0704; i < 0x0744; i += 4)
- nv_wo32(dev, ctx, i/4, 0x01012000);
+ nv_wo32(ctx, i, 0x01012000);
for (i = 0x0744; i < 0x0784; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00080008);
- nv_wo32(dev, ctx, 0x0860/4, 0x00040000);
- nv_wo32(dev, ctx, 0x0864/4, 0x00010000);
+ nv_wo32(ctx, i, 0x00080008);
+ nv_wo32(ctx, 0x0860, 0x00040000);
+ nv_wo32(ctx, 0x0864, 0x00010000);
for (i = 0x0868; i < 0x0878; i += 4)
- nv_wo32(dev, ctx, i/4, 0x00040004);
+ nv_wo32(ctx, i, 0x00040004);
for (i = 0x1f1c; i <= 0x308c ; i += 16) {
- nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
- nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
- nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
+ nv_wo32(ctx, i + 0, 0x10700ff9);
+ nv_wo32(ctx, i + 4, 0x0436086c);
+ nv_wo32(ctx, i + 8, 0x000c001b);
}
for (i = 0x30bc; i < 0x30cc; i += 4)
- nv_wo32(dev, ctx, i/4, 0x0000ffff);
- nv_wo32(dev, ctx, 0x3450/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x380c/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x3820/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x384c/4, 0x40000000);
- nv_wo32(dev, ctx, 0x3850/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x3854/4, 0x3f000000);
- nv_wo32(dev, ctx, 0x385c/4, 0x40000000);
- nv_wo32(dev, ctx, 0x3860/4, 0x3f800000);
- nv_wo32(dev, ctx, 0x3868/4, 0xbf800000);
- nv_wo32(dev, ctx, 0x3870/4, 0xbf800000);
+ nv_wo32(ctx, i, 0x0000ffff);
+ nv_wo32(ctx, 0x3450, 0x3f800000);
+ nv_wo32(ctx, 0x380c, 0x3f800000);
+ nv_wo32(ctx, 0x3820, 0x3f800000);
+ nv_wo32(ctx, 0x384c, 0x40000000);
+ nv_wo32(ctx, 0x3850, 0x3f800000);
+ nv_wo32(ctx, 0x3854, 0x3f000000);
+ nv_wo32(ctx, 0x385c, 0x40000000);
+ nv_wo32(ctx, 0x3860, 0x3f800000);
+ nv_wo32(ctx, 0x3868, 0xbf800000);
+ nv_wo32(ctx, 0x3870, 0xbf800000);
}
int
@@ -372,7 +372,7 @@ nv20_graph_create_context(struct nouveau_channel *chan)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
- unsigned int idoffs = 0x28/4;
+ unsigned int idoffs = 0x28;
int ret;
switch (dev_priv->chipset) {
@@ -403,21 +403,19 @@ nv20_graph_create_context(struct nouveau_channel *chan)
BUG_ON(1);
}
- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
- 16, NVOBJ_FLAG_ZERO_ALLOC,
- &chan->ramin_grctx);
+ ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16,
+ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx);
if (ret)
return ret;
/* Initialise default context values */
- ctx_init(dev, chan->ramin_grctx->gpuobj);
+ ctx_init(dev, chan->ramin_grctx);
/* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
- nv_wo32(dev, chan->ramin_grctx->gpuobj, idoffs,
- (chan->id << 24) | 0x1); /* CTX_USER */
+ nv_wo32(chan->ramin_grctx, idoffs,
+ (chan->id << 24) | 0x1); /* CTX_USER */
- nv_wo32(dev, pgraph->ctx_table->gpuobj, chan->id,
- chan->ramin_grctx->instance >> 4);
+ nv_wo32(pgraph->ctx_table, chan->id * 4, chan->ramin_grctx->pinst >> 4);
return 0;
}
@@ -428,10 +426,8 @@ nv20_graph_destroy_context(struct nouveau_channel *chan)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- if (chan->ramin_grctx)
- nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
-
- nv_wo32(dev, pgraph->ctx_table->gpuobj, chan->id, 0);
+ nouveau_gpuobj_ref(NULL, &chan->ramin_grctx);
+ nv_wo32(pgraph->ctx_table, chan->id * 4, 0);
}
int
@@ -442,7 +438,7 @@ nv20_graph_load_context(struct nouveau_channel *chan)
if (!chan->ramin_grctx)
return -EINVAL;
- inst = chan->ramin_grctx->instance >> 4;
+ inst = chan->ramin_grctx->pinst >> 4;
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
@@ -465,7 +461,7 @@ nv20_graph_unload_context(struct drm_device *dev)
chan = pgraph->channel(dev);
if (!chan)
return 0;
- inst = chan->ramin_grctx->instance >> 4;
+ inst = chan->ramin_grctx->pinst >> 4;
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
@@ -552,15 +548,15 @@ nv20_graph_init(struct drm_device *dev)
if (!pgraph->ctx_table) {
/* Create Context Pointer Table */
- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32 * 4, 16,
- NVOBJ_FLAG_ZERO_ALLOC,
- &pgraph->ctx_table);
+ ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16,
+ NVOBJ_FLAG_ZERO_ALLOC,
+ &pgraph->ctx_table);
if (ret)
return ret;
}
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
- pgraph->ctx_table->instance >> 4);
+ pgraph->ctx_table->pinst >> 4);
nv20_graph_rdi(dev);
@@ -646,7 +642,7 @@ nv20_graph_takedown(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- nouveau_gpuobj_ref_del(dev, &pgraph->ctx_table);
+ nouveau_gpuobj_ref(NULL, &pgraph->ctx_table);
}
int
@@ -681,15 +677,15 @@ nv30_graph_init(struct drm_device *dev)
if (!pgraph->ctx_table) {
/* Create Context Pointer Table */
- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32 * 4, 16,
- NVOBJ_FLAG_ZERO_ALLOC,
- &pgraph->ctx_table);
+ ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16,
+ NVOBJ_FLAG_ZERO_ALLOC,
+ &pgraph->ctx_table);
if (ret)
return ret;
}
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
- pgraph->ctx_table->instance >> 4);
+ pgraph->ctx_table->pinst >> 4);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c
index 2b67f1835c39..d337b8b28cdd 100644
--- a/drivers/gpu/drm/nouveau/nv40_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv40_fifo.c
@@ -27,8 +27,9 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
+#include "nouveau_ramht.h"
-#define NV40_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV40_RAMFC__SIZE))
+#define NV40_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV40_RAMFC__SIZE))
#define NV40_RAMFC__SIZE 128
int
@@ -42,7 +43,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
NV40_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
- NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
+ NVOBJ_FLAG_ZERO_FREE, &chan->ramfc);
if (ret)
return ret;
@@ -50,7 +51,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
nv_wi32(dev, fc + 0, chan->pushbuf_base);
nv_wi32(dev, fc + 4, chan->pushbuf_base);
- nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
+ nv_wi32(dev, fc + 12, chan->pushbuf->pinst >> 4);
nv_wi32(dev, fc + 24, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
@@ -58,7 +59,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
NV_PFIFO_CACHE1_BIG_ENDIAN |
#endif
0x30000000 /* no idea.. */);
- nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4);
+ nv_wi32(dev, fc + 56, chan->ramin_grctx->pinst >> 4);
nv_wi32(dev, fc + 60, 0x0001FFFF);
/* enable the fifo dma operation */
@@ -77,8 +78,7 @@ nv40_fifo_destroy_context(struct nouveau_channel *chan)
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
- if (chan->ramfc)
- nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+ nouveau_gpuobj_ref(NULL, &chan->ramfc);
}
static void
@@ -241,9 +241,9 @@ nv40_fifo_init_ramxx(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
- ((dev_priv->ramht_bits - 9) << 16) |
- (dev_priv->ramht_offset >> 8));
- nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
+ ((dev_priv->ramht->bits - 9) << 16) |
+ (dev_priv->ramht->gpuobj->pinst >> 8));
+ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8);
switch (dev_priv->chipset) {
case 0x47:
@@ -271,7 +271,7 @@ nv40_fifo_init_ramxx(struct drm_device *dev)
nv_wr32(dev, 0x2230, 0);
nv_wr32(dev, NV40_PFIFO_RAMFC,
((dev_priv->vram_size - 512 * 1024 +
- dev_priv->ramfc_offset) >> 16) | (3 << 16));
+ dev_priv->ramfc->pinst) >> 16) | (3 << 16));
break;
}
}
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
index fd7d2b501316..7ee1b91569b8 100644
--- a/drivers/gpu/drm/nouveau/nv40_graph.c
+++ b/drivers/gpu/drm/nouveau/nv40_graph.c
@@ -45,7 +45,7 @@ nv40_graph_channel(struct drm_device *dev)
struct nouveau_channel *chan = dev_priv->fifos[i];
if (chan && chan->ramin_grctx &&
- chan->ramin_grctx->instance == inst)
+ chan->ramin_grctx->pinst == inst)
return chan;
}
@@ -61,27 +61,25 @@ nv40_graph_create_context(struct nouveau_channel *chan)
struct nouveau_grctx ctx = {};
int ret;
- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
- 16, NVOBJ_FLAG_ZERO_ALLOC,
- &chan->ramin_grctx);
+ ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16,
+ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx);
if (ret)
return ret;
/* Initialise default context values */
ctx.dev = chan->dev;
ctx.mode = NOUVEAU_GRCTX_VALS;
- ctx.data = chan->ramin_grctx->gpuobj;
+ ctx.data = chan->ramin_grctx;
nv40_grctx_init(&ctx);
- nv_wo32(dev, chan->ramin_grctx->gpuobj, 0,
- chan->ramin_grctx->gpuobj->im_pramin->start);
+ nv_wo32(chan->ramin_grctx, 0, chan->ramin_grctx->pinst);
return 0;
}
void
nv40_graph_destroy_context(struct nouveau_channel *chan)
{
- nouveau_gpuobj_ref_del(chan->dev, &chan->ramin_grctx);
+ nouveau_gpuobj_ref(NULL, &chan->ramin_grctx);
}
static int
@@ -135,7 +133,7 @@ nv40_graph_load_context(struct nouveau_channel *chan)
if (!chan->ramin_grctx)
return -EINVAL;
- inst = chan->ramin_grctx->instance >> 4;
+ inst = chan->ramin_grctx->pinst >> 4;
ret = nv40_graph_transfer_context(dev, inst, 0);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/nv40_grctx.c b/drivers/gpu/drm/nouveau/nv40_grctx.c
index 9b5c97469588..ce585093264e 100644
--- a/drivers/gpu/drm/nouveau/nv40_grctx.c
+++ b/drivers/gpu/drm/nouveau/nv40_grctx.c
@@ -596,13 +596,13 @@ nv40_graph_construct_shader(struct nouveau_grctx *ctx)
offset += 0x0280/4;
for (i = 0; i < 16; i++, offset += 2)
- nv_wo32(dev, obj, offset, 0x3f800000);
+ nv_wo32(obj, offset * 4, 0x3f800000);
for (vs = 0; vs < vs_nr; vs++, offset += vs_len) {
for (i = 0; i < vs_nr_b0 * 6; i += 6)
- nv_wo32(dev, obj, offset + b0_offset + i, 0x00000001);
+ nv_wo32(obj, (offset + b0_offset + i) * 4, 0x00000001);
for (i = 0; i < vs_nr_b1 * 4; i += 4)
- nv_wo32(dev, obj, offset + b1_offset + i, 0x3f800000);
+ nv_wo32(obj, (offset + b1_offset + i) * 4, 0x3f800000);
}
}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index bfd4ca2fe7ef..16380d52cd88 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -104,8 +104,7 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
OUT_RING(evo, nv_crtc->lut.depth == 8 ?
NV50_EVO_CRTC_CLUT_MODE_OFF :
NV50_EVO_CRTC_CLUT_MODE_ON);
- OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.mm_node->start <<
- PAGE_SHIFT) >> 8);
+ OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.start << PAGE_SHIFT) >> 8);
if (dev_priv->chipset != 0x50) {
BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
OUT_RING(evo, NvEvoVRAM);
@@ -266,15 +265,10 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct pll_lims pll;
- uint32_t reg, reg1, reg2;
+ uint32_t reg1, reg2;
int ret, N1, M1, N2, M2, P;
- if (dev_priv->chipset < NV_C0)
- reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
- else
- reg = 0x614140 + (head * 0x800);
-
- ret = get_pll_limits(dev, reg, &pll);
+ ret = get_pll_limits(dev, PLL_VPLL0 + head, &pll);
if (ret)
return ret;
@@ -286,11 +280,11 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
NV_DEBUG(dev, "pclk %d out %d NM1 %d %d NM2 %d %d P %d\n",
pclk, ret, N1, M1, N2, M2, P);
- reg1 = nv_rd32(dev, reg + 4) & 0xff00ff00;
- reg2 = nv_rd32(dev, reg + 8) & 0x8000ff00;
- nv_wr32(dev, reg, 0x10000611);
- nv_wr32(dev, reg + 4, reg1 | (M1 << 16) | N1);
- nv_wr32(dev, reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
+ reg1 = nv_rd32(dev, pll.reg + 4) & 0xff00ff00;
+ reg2 = nv_rd32(dev, pll.reg + 8) & 0x8000ff00;
+ nv_wr32(dev, pll.reg + 0, 0x10000611);
+ nv_wr32(dev, pll.reg + 4, reg1 | (M1 << 16) | N1);
+ nv_wr32(dev, pll.reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
} else
if (dev_priv->chipset < NV_C0) {
ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
@@ -300,10 +294,10 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
pclk, ret, N1, N2, M1, P);
- reg1 = nv_rd32(dev, reg + 4) & 0xffc00000;
- nv_wr32(dev, reg, 0x50000610);
- nv_wr32(dev, reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
- nv_wr32(dev, reg + 8, N2);
+ reg1 = nv_rd32(dev, pll.reg + 4) & 0xffc00000;
+ nv_wr32(dev, pll.reg + 0, 0x50000610);
+ nv_wr32(dev, pll.reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
+ nv_wr32(dev, pll.reg + 8, N2);
} else {
ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
if (ret <= 0)
@@ -312,9 +306,9 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
pclk, ret, N1, N2, M1, P);
- nv_mask(dev, reg + 0x0c, 0x00000000, 0x00000100);
- nv_wr32(dev, reg + 0x04, (P << 16) | (N1 << 8) | M1);
- nv_wr32(dev, reg + 0x10, N2 << 16);
+ nv_mask(dev, pll.reg + 0x0c, 0x00000000, 0x00000100);
+ nv_wr32(dev, pll.reg + 0x04, (P << 16) | (N1 << 8) | M1);
+ nv_wr32(dev, pll.reg + 0x10, N2 << 16);
}
return 0;
@@ -338,7 +332,9 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
nv50_cursor_fini(nv_crtc);
+ nouveau_bo_unmap(nv_crtc->lut.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
+ nouveau_bo_unmap(nv_crtc->cursor.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
kfree(nv_crtc->mode);
kfree(nv_crtc);
@@ -491,8 +487,9 @@ nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
}
static int
-nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y,
- struct drm_framebuffer *old_fb, bool update)
+nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *passed_fb,
+ int x, int y, bool update, bool atomic)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct drm_device *dev = nv_crtc->base.dev;
@@ -504,6 +501,28 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y,
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
+ /* If atomic, we want to switch to the fb we were passed, so
+ * now we update pointers to do that. (We don't pin; just
+ * assume we're already pinned and update the base address.)
+ */
+ if (atomic) {
+ drm_fb = passed_fb;
+ fb = nouveau_framebuffer(passed_fb);
+ }
+ else {
+ /* If not atomic, we can go ahead and pin, and unpin the
+ * old fb we were passed.
+ */
+ ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
+ if (ret)
+ return ret;
+
+ if (passed_fb) {
+ struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb);
+ nouveau_bo_unpin(ofb->nvbo);
+ }
+ }
+
switch (drm_fb->depth) {
case 8:
format = NV50_EVO_CRTC_FB_DEPTH_8;
@@ -526,15 +545,6 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y,
return -EINVAL;
}
- ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
- if (ret)
- return ret;
-
- if (old_fb) {
- struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
- nouveau_bo_unpin(ofb->nvbo);
- }
-
nv_crtc->fb.offset = fb->nvbo->bo.offset - dev_priv->vm_vram_base;
nv_crtc->fb.tile_flags = fb->nvbo->tile_flags;
nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
@@ -685,14 +695,22 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
- return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, false);
+ return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false, false);
}
static int
nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
- return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, true);
+ return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, true, false);
+}
+
+static int
+nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, enum mode_set_atomic state)
+{
+ return nv50_crtc_do_mode_set_base(crtc, fb, x, y, true, true);
}
static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
@@ -702,6 +720,7 @@ static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
.mode_fixup = nv50_crtc_mode_fixup,
.mode_set = nv50_crtc_mode_set,
.mode_set_base = nv50_crtc_mode_set_base,
+ .mode_set_base_atomic = nv50_crtc_mode_set_base_atomic,
.load_lut = nv50_crtc_lut_load,
};
diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c
index 03ad7ab14f09..1b9ce3021aa3 100644
--- a/drivers/gpu/drm/nouveau/nv50_cursor.c
+++ b/drivers/gpu/drm/nouveau/nv50_cursor.c
@@ -147,7 +147,7 @@ nv50_cursor_fini(struct nouveau_crtc *nv_crtc)
NV_DEBUG_KMS(dev, "\n");
nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0);
- if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx),
+ if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx),
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
index 1bc085962945..875414b09ade 100644
--- a/drivers/gpu/drm/nouveau/nv50_dac.c
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
@@ -79,7 +79,7 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
0x00150000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
- if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
+ if (!nv_wait(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
@@ -130,7 +130,7 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode)
NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
/* wait for it to be done */
- if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
+ if (!nv_wait(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 612fa6d6a0cb..55c9663ef2bf 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -30,8 +30,22 @@
#include "nouveau_connector.h"
#include "nouveau_fb.h"
#include "nouveau_fbcon.h"
+#include "nouveau_ramht.h"
#include "drm_crtc_helper.h"
+static inline int
+nv50_sor_nr(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->chipset < 0x90 ||
+ dev_priv->chipset == 0x92 ||
+ dev_priv->chipset == 0xa0)
+ return 2;
+
+ return 4;
+}
+
static void
nv50_evo_channel_del(struct nouveau_channel **pchan)
{
@@ -42,6 +56,7 @@ nv50_evo_channel_del(struct nouveau_channel **pchan)
*pchan = NULL;
nouveau_gpuobj_channel_takedown(chan);
+ nouveau_bo_unmap(chan->pushbuf_bo);
nouveau_bo_ref(NULL, &chan->pushbuf_bo);
if (chan->user)
@@ -65,23 +80,23 @@ nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name,
return ret;
obj->engine = NVOBJ_ENGINE_DISPLAY;
- ret = nouveau_gpuobj_ref_add(dev, evo, name, obj, NULL);
- if (ret) {
- nouveau_gpuobj_del(dev, &obj);
- return ret;
- }
-
- nv_wo32(dev, obj, 0, (tile_flags << 22) | (magic_flags << 16) | class);
- nv_wo32(dev, obj, 1, limit);
- nv_wo32(dev, obj, 2, offset);
- nv_wo32(dev, obj, 3, 0x00000000);
- nv_wo32(dev, obj, 4, 0x00000000);
+ nv_wo32(obj, 0, (tile_flags << 22) | (magic_flags << 16) | class);
+ nv_wo32(obj, 4, limit);
+ nv_wo32(obj, 8, offset);
+ nv_wo32(obj, 12, 0x00000000);
+ nv_wo32(obj, 16, 0x00000000);
if (dev_priv->card_type < NV_C0)
- nv_wo32(dev, obj, 5, 0x00010000);
+ nv_wo32(obj, 20, 0x00010000);
else
- nv_wo32(dev, obj, 5, 0x00020000);
+ nv_wo32(obj, 20, 0x00020000);
dev_priv->engine.instmem.flush(dev);
+ ret = nouveau_ramht_insert(evo, name, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ if (ret) {
+ return ret;
+ }
+
return 0;
}
@@ -89,6 +104,7 @@ static int
nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ramht = NULL;
struct nouveau_channel *chan;
int ret;
@@ -102,32 +118,35 @@ nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan)
chan->user_get = 4;
chan->user_put = 0;
- INIT_LIST_HEAD(&chan->ramht_refs);
-
- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32768, 0x1000,
- NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin);
+ ret = nouveau_gpuobj_new(dev, NULL, 32768, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin);
if (ret) {
NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret);
nv50_evo_channel_del(pchan);
return ret;
}
- ret = drm_mm_init(&chan->ramin_heap,
- chan->ramin->gpuobj->im_pramin->start, 32768);
+ ret = drm_mm_init(&chan->ramin_heap, 0, 32768);
if (ret) {
NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret);
nv50_evo_channel_del(pchan);
return ret;
}
- ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 4096, 16,
- 0, &chan->ramht);
+ ret = nouveau_gpuobj_new(dev, chan, 4096, 16, 0, &ramht);
if (ret) {
NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret);
nv50_evo_channel_del(pchan);
return ret;
}
+ ret = nouveau_ramht_new(dev, ramht, &chan->ramht);
+ nouveau_gpuobj_ref(NULL, &ramht);
+ if (ret) {
+ nv50_evo_channel_del(pchan);
+ return ret;
+ }
+
if (dev_priv->chipset != 0x50) {
ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB16, 0x70, 0x19,
0, 0xffffffff);
@@ -227,11 +246,11 @@ nv50_display_init(struct drm_device *dev)
nv_wr32(dev, 0x006101d0 + (i * 0x04), val);
}
/* SOR */
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < nv50_sor_nr(dev); i++) {
val = nv_rd32(dev, 0x0061c000 + (i * 0x800));
nv_wr32(dev, 0x006101e0 + (i * 0x04), val);
}
- /* Something not yet in use, tv-out maybe. */
+ /* EXT */
for (i = 0; i < 3; i++) {
val = nv_rd32(dev, 0x0061e000 + (i * 0x800));
nv_wr32(dev, 0x006101f0 + (i * 0x04), val);
@@ -260,7 +279,7 @@ nv50_display_init(struct drm_device *dev)
if (nv_rd32(dev, NV50_PDISPLAY_INTR_1) & 0x100) {
nv_wr32(dev, NV50_PDISPLAY_INTR_1, 0x100);
nv_wr32(dev, 0x006194e8, nv_rd32(dev, 0x006194e8) & ~1);
- if (!nv_wait(0x006194e8, 2, 0)) {
+ if (!nv_wait(dev, 0x006194e8, 2, 0)) {
NV_ERROR(dev, "timeout: (0x6194e8 & 2) != 0\n");
NV_ERROR(dev, "0x6194e8 = 0x%08x\n",
nv_rd32(dev, 0x6194e8));
@@ -291,7 +310,8 @@ nv50_display_init(struct drm_device *dev)
nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE);
nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x1000b03);
- if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x40000000, 0x40000000)) {
+ if (!nv_wait(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
+ 0x40000000, 0x40000000)) {
NV_ERROR(dev, "timeout: (0x610200 & 0x40000000) == 0x40000000\n");
NV_ERROR(dev, "0x610200 = 0x%08x\n",
nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
@@ -300,7 +320,7 @@ nv50_display_init(struct drm_device *dev)
for (i = 0; i < 2; i++) {
nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000);
- if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
+ if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
@@ -310,7 +330,7 @@ nv50_display_init(struct drm_device *dev)
nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON);
- if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
+ if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS,
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) {
NV_ERROR(dev, "timeout: "
@@ -321,16 +341,16 @@ nv50_display_init(struct drm_device *dev)
}
}
- nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->instance >> 8) | 9);
+ nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9);
/* initialise fifo */
nv_wr32(dev, NV50_PDISPLAY_CHANNEL_DMA_CB(0),
- ((evo->pushbuf_bo->bo.mem.mm_node->start << PAGE_SHIFT) >> 8) |
+ ((evo->pushbuf_bo->bo.mem.start << PAGE_SHIFT) >> 8) |
NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM |
NV50_PDISPLAY_CHANNEL_DMA_CB_VALID);
nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK2(0), 0x00010000);
nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK3(0), 0x00000002);
- if (!nv_wait(0x610200, 0x80000000, 0x00000000)) {
+ if (!nv_wait(dev, 0x610200, 0x80000000, 0x00000000)) {
NV_ERROR(dev, "timeout: (0x610200 & 0x80000000) == 0\n");
NV_ERROR(dev, "0x610200 = 0x%08x\n", nv_rd32(dev, 0x610200));
return -EBUSY;
@@ -370,7 +390,7 @@ nv50_display_init(struct drm_device *dev)
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1);
OUT_RING(evo, 0);
FIRE_RING(evo);
- if (!nv_wait(0x640004, 0xffffffff, evo->dma.put << 2))
+ if (!nv_wait(dev, 0x640004, 0xffffffff, evo->dma.put << 2))
NV_ERROR(dev, "evo pushbuf stalled\n");
/* enable clock change interrupts. */
@@ -424,7 +444,7 @@ static int nv50_display_disable(struct drm_device *dev)
continue;
nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask);
- if (!nv_wait(NV50_PDISPLAY_INTR_1, mask, mask)) {
+ if (!nv_wait(dev, NV50_PDISPLAY_INTR_1, mask, mask)) {
NV_ERROR(dev, "timeout: (0x610024 & 0x%08x) == "
"0x%08x\n", mask, mask);
NV_ERROR(dev, "0x610024 = 0x%08x\n",
@@ -434,14 +454,14 @@ static int nv50_display_disable(struct drm_device *dev)
nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0);
nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, 0);
- if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x1e0000, 0)) {
+ if (!nv_wait(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x1e0000, 0)) {
NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) == 0\n");
NV_ERROR(dev, "0x610200 = 0x%08x\n",
nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
}
for (i = 0; i < 3; i++) {
- if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(i),
+ if (!nv_wait(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i),
NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i);
NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", i,
@@ -710,7 +730,7 @@ nv50_display_unk10_handler(struct drm_device *dev)
or = i;
}
- for (i = 0; type == OUTPUT_ANY && i < 4; i++) {
+ for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) {
if (dev_priv->chipset < 0x90 ||
dev_priv->chipset == 0x92 ||
dev_priv->chipset == 0xa0)
@@ -841,7 +861,7 @@ nv50_display_unk20_handler(struct drm_device *dev)
or = i;
}
- for (i = 0; type == OUTPUT_ANY && i < 4; i++) {
+ for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) {
if (dev_priv->chipset < 0x90 ||
dev_priv->chipset == 0x92 ||
dev_priv->chipset == 0xa0)
diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c
index 32611bd30e6d..cd1988b15d2c 100644
--- a/drivers/gpu/drm/nouveau/nv50_fb.c
+++ b/drivers/gpu/drm/nouveau/nv50_fb.c
@@ -20,6 +20,7 @@ nv50_fb_init(struct drm_device *dev)
case 0x50:
nv_wr32(dev, 0x100c90, 0x0707ff);
break;
+ case 0xa3:
case 0xa5:
case 0xa8:
nv_wr32(dev, 0x100c90, 0x0d0fff);
@@ -36,3 +37,42 @@ void
nv50_fb_takedown(struct drm_device *dev)
{
}
+
+void
+nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 trap[6], idx, chinst;
+ int i, ch;
+
+ idx = nv_rd32(dev, 0x100c90);
+ if (!(idx & 0x80000000))
+ return;
+ idx &= 0x00ffffff;
+
+ for (i = 0; i < 6; i++) {
+ nv_wr32(dev, 0x100c90, idx | i << 24);
+ trap[i] = nv_rd32(dev, 0x100c94);
+ }
+ nv_wr32(dev, 0x100c90, idx | 0x80000000);
+
+ if (!display)
+ return;
+
+ chinst = (trap[2] << 16) | trap[1];
+ for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) {
+ struct nouveau_channel *chan = dev_priv->fifos[ch];
+
+ if (!chan || !chan->ramin)
+ continue;
+
+ if (chinst == chan->ramin->vinst >> 12)
+ break;
+ }
+
+ NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x "
+ "channel %d (0x%08x)\n",
+ name, (trap[5] & 0x100 ? "read" : "write"),
+ trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff,
+ trap[0], ch, chinst);
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
index 6bf025c6fc6f..6dcf048eddbc 100644
--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -1,6 +1,7 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
+#include "nouveau_ramht.h"
#include "nouveau_fbcon.h"
void
@@ -193,7 +194,8 @@ nv50_fbcon_accel_init(struct fb_info *info)
if (ret)
return ret;
- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, Nv2D, eng2d, NULL);
+ ret = nouveau_ramht_insert(dev_priv->channel, Nv2D, eng2d);
+ nouveau_gpuobj_ref(NULL, &eng2d);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
index fb0281ae8f90..a46a961102f3 100644
--- a/drivers/gpu/drm/nouveau/nv50_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
@@ -27,13 +27,14 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
static void
nv50_fifo_playlist_update(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
- struct nouveau_gpuobj_ref *cur;
+ struct nouveau_gpuobj *cur;
int i, nr;
NV_DEBUG(dev, "\n");
@@ -43,12 +44,14 @@ nv50_fifo_playlist_update(struct drm_device *dev)
/* We never schedule channel 0 or 127 */
for (i = 1, nr = 0; i < 127; i++) {
- if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc)
- nv_wo32(dev, cur->gpuobj, nr++, i);
+ if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc) {
+ nv_wo32(cur, (nr * 4), i);
+ nr++;
+ }
}
dev_priv->engine.instmem.flush(dev);
- nv_wr32(dev, 0x32f4, cur->instance >> 12);
+ nv_wr32(dev, 0x32f4, cur->vinst >> 12);
nv_wr32(dev, 0x32ec, nr);
nv_wr32(dev, 0x2500, 0x101);
}
@@ -63,9 +66,9 @@ nv50_fifo_channel_enable(struct drm_device *dev, int channel)
NV_DEBUG(dev, "ch%d\n", channel);
if (dev_priv->chipset == 0x50)
- inst = chan->ramfc->instance >> 12;
+ inst = chan->ramfc->vinst >> 12;
else
- inst = chan->ramfc->instance >> 8;
+ inst = chan->ramfc->vinst >> 8;
nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst |
NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED);
@@ -163,19 +166,19 @@ nv50_fifo_init(struct drm_device *dev)
goto just_reset;
}
- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
- NVOBJ_FLAG_ZERO_ALLOC,
- &pfifo->playlist[0]);
+ ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC,
+ &pfifo->playlist[0]);
if (ret) {
NV_ERROR(dev, "error creating playlist 0: %d\n", ret);
return ret;
}
- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
- NVOBJ_FLAG_ZERO_ALLOC,
- &pfifo->playlist[1]);
+ ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC,
+ &pfifo->playlist[1]);
if (ret) {
- nouveau_gpuobj_ref_del(dev, &pfifo->playlist[0]);
+ nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]);
NV_ERROR(dev, "error creating playlist 1: %d\n", ret);
return ret;
}
@@ -203,8 +206,8 @@ nv50_fifo_takedown(struct drm_device *dev)
if (!pfifo->playlist[0])
return;
- nouveau_gpuobj_ref_del(dev, &pfifo->playlist[0]);
- nouveau_gpuobj_ref_del(dev, &pfifo->playlist[1]);
+ nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]);
+ nouveau_gpuobj_ref(NULL, &pfifo->playlist[1]);
}
int
@@ -226,59 +229,54 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
NV_DEBUG(dev, "ch%d\n", chan->id);
if (dev_priv->chipset == 0x50) {
- uint32_t ramin_poffset = chan->ramin->gpuobj->im_pramin->start;
- uint32_t ramin_voffset = chan->ramin->gpuobj->im_backing_start;
-
- ret = nouveau_gpuobj_new_fake(dev, ramin_poffset, ramin_voffset,
- 0x100, NVOBJ_FLAG_ZERO_ALLOC |
- NVOBJ_FLAG_ZERO_FREE, &ramfc,
+ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst,
+ chan->ramin->vinst, 0x100,
+ NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE,
&chan->ramfc);
if (ret)
return ret;
- ret = nouveau_gpuobj_new_fake(dev, ramin_poffset + 0x0400,
- ramin_voffset + 0x0400, 4096,
- 0, NULL, &chan->cache);
+ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst + 0x0400,
+ chan->ramin->vinst + 0x0400,
+ 4096, 0, &chan->cache);
if (ret)
return ret;
} else {
- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 0x100, 256,
- NVOBJ_FLAG_ZERO_ALLOC |
- NVOBJ_FLAG_ZERO_FREE,
- &chan->ramfc);
+ ret = nouveau_gpuobj_new(dev, chan, 0x100, 256,
+ NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &chan->ramfc);
if (ret)
return ret;
- ramfc = chan->ramfc->gpuobj;
- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 1024,
- 0, &chan->cache);
+ ret = nouveau_gpuobj_new(dev, chan, 4096, 1024,
+ 0, &chan->cache);
if (ret)
return ret;
}
+ ramfc = chan->ramfc;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
- nv_wo32(dev, ramfc, 0x80/4, (0 << 27) /* 4KiB */ |
- (4 << 24) /* SEARCH_FULL */ |
- (chan->ramht->instance >> 4));
- nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
- nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
- nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
- nv_wo32(dev, ramfc, 0x7c/4, 0x30000001);
- nv_wo32(dev, ramfc, 0x78/4, 0x00000000);
- nv_wo32(dev, ramfc, 0x3c/4, 0x403f6078);
- nv_wo32(dev, ramfc, 0x50/4, chan->pushbuf_base +
- chan->dma.ib_base * 4);
- nv_wo32(dev, ramfc, 0x54/4, drm_order(chan->dma.ib_max + 1) << 16);
+ nv_wo32(ramfc, 0x48, chan->pushbuf->cinst >> 4);
+ nv_wo32(ramfc, 0x80, ((chan->ramht->bits - 9) << 27) |
+ (4 << 24) /* SEARCH_FULL */ |
+ (chan->ramht->gpuobj->cinst >> 4));
+ nv_wo32(ramfc, 0x44, 0x2101ffff);
+ nv_wo32(ramfc, 0x60, 0x7fffffff);
+ nv_wo32(ramfc, 0x40, 0x00000000);
+ nv_wo32(ramfc, 0x7c, 0x30000001);
+ nv_wo32(ramfc, 0x78, 0x00000000);
+ nv_wo32(ramfc, 0x3c, 0x403f6078);
+ nv_wo32(ramfc, 0x50, chan->pushbuf_base + chan->dma.ib_base * 4);
+ nv_wo32(ramfc, 0x54, drm_order(chan->dma.ib_max + 1) << 16);
if (dev_priv->chipset != 0x50) {
- nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
- nv_wo32(dev, chan->ramin->gpuobj, 1,
- chan->ramfc->instance >> 8);
+ nv_wo32(chan->ramin, 0, chan->id);
+ nv_wo32(chan->ramin, 4, chan->ramfc->vinst >> 8);
- nv_wo32(dev, ramfc, 0x88/4, chan->cache->instance >> 10);
- nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12);
+ nv_wo32(ramfc, 0x88, chan->cache->vinst >> 10);
+ nv_wo32(ramfc, 0x98, chan->ramin->vinst >> 12);
}
dev_priv->engine.instmem.flush(dev);
@@ -293,12 +291,13 @@ void
nv50_fifo_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
- struct nouveau_gpuobj_ref *ramfc = chan->ramfc;
+ struct nouveau_gpuobj *ramfc = NULL;
NV_DEBUG(dev, "ch%d\n", chan->id);
/* This will ensure the channel is seen as disabled. */
- chan->ramfc = NULL;
+ nouveau_gpuobj_ref(chan->ramfc, &ramfc);
+ nouveau_gpuobj_ref(NULL, &chan->ramfc);
nv50_fifo_channel_disable(dev, chan->id);
/* Dummy channel, also used on ch 127 */
@@ -306,8 +305,8 @@ nv50_fifo_destroy_context(struct nouveau_channel *chan)
nv50_fifo_channel_disable(dev, 127);
nv50_fifo_playlist_update(dev);
- nouveau_gpuobj_ref_del(dev, &ramfc);
- nouveau_gpuobj_ref_del(dev, &chan->cache);
+ nouveau_gpuobj_ref(NULL, &ramfc);
+ nouveau_gpuobj_ref(NULL, &chan->cache);
}
int
@@ -315,63 +314,63 @@ nv50_fifo_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj;
- struct nouveau_gpuobj *cache = chan->cache->gpuobj;
+ struct nouveau_gpuobj *ramfc = chan->ramfc;
+ struct nouveau_gpuobj *cache = chan->cache;
int ptr, cnt;
NV_DEBUG(dev, "ch%d\n", chan->id);
- nv_wr32(dev, 0x3330, nv_ro32(dev, ramfc, 0x00/4));
- nv_wr32(dev, 0x3334, nv_ro32(dev, ramfc, 0x04/4));
- nv_wr32(dev, 0x3240, nv_ro32(dev, ramfc, 0x08/4));
- nv_wr32(dev, 0x3320, nv_ro32(dev, ramfc, 0x0c/4));
- nv_wr32(dev, 0x3244, nv_ro32(dev, ramfc, 0x10/4));
- nv_wr32(dev, 0x3328, nv_ro32(dev, ramfc, 0x14/4));
- nv_wr32(dev, 0x3368, nv_ro32(dev, ramfc, 0x18/4));
- nv_wr32(dev, 0x336c, nv_ro32(dev, ramfc, 0x1c/4));
- nv_wr32(dev, 0x3370, nv_ro32(dev, ramfc, 0x20/4));
- nv_wr32(dev, 0x3374, nv_ro32(dev, ramfc, 0x24/4));
- nv_wr32(dev, 0x3378, nv_ro32(dev, ramfc, 0x28/4));
- nv_wr32(dev, 0x337c, nv_ro32(dev, ramfc, 0x2c/4));
- nv_wr32(dev, 0x3228, nv_ro32(dev, ramfc, 0x30/4));
- nv_wr32(dev, 0x3364, nv_ro32(dev, ramfc, 0x34/4));
- nv_wr32(dev, 0x32a0, nv_ro32(dev, ramfc, 0x38/4));
- nv_wr32(dev, 0x3224, nv_ro32(dev, ramfc, 0x3c/4));
- nv_wr32(dev, 0x324c, nv_ro32(dev, ramfc, 0x40/4));
- nv_wr32(dev, 0x2044, nv_ro32(dev, ramfc, 0x44/4));
- nv_wr32(dev, 0x322c, nv_ro32(dev, ramfc, 0x48/4));
- nv_wr32(dev, 0x3234, nv_ro32(dev, ramfc, 0x4c/4));
- nv_wr32(dev, 0x3340, nv_ro32(dev, ramfc, 0x50/4));
- nv_wr32(dev, 0x3344, nv_ro32(dev, ramfc, 0x54/4));
- nv_wr32(dev, 0x3280, nv_ro32(dev, ramfc, 0x58/4));
- nv_wr32(dev, 0x3254, nv_ro32(dev, ramfc, 0x5c/4));
- nv_wr32(dev, 0x3260, nv_ro32(dev, ramfc, 0x60/4));
- nv_wr32(dev, 0x3264, nv_ro32(dev, ramfc, 0x64/4));
- nv_wr32(dev, 0x3268, nv_ro32(dev, ramfc, 0x68/4));
- nv_wr32(dev, 0x326c, nv_ro32(dev, ramfc, 0x6c/4));
- nv_wr32(dev, 0x32e4, nv_ro32(dev, ramfc, 0x70/4));
- nv_wr32(dev, 0x3248, nv_ro32(dev, ramfc, 0x74/4));
- nv_wr32(dev, 0x2088, nv_ro32(dev, ramfc, 0x78/4));
- nv_wr32(dev, 0x2058, nv_ro32(dev, ramfc, 0x7c/4));
- nv_wr32(dev, 0x2210, nv_ro32(dev, ramfc, 0x80/4));
-
- cnt = nv_ro32(dev, ramfc, 0x84/4);
+ nv_wr32(dev, 0x3330, nv_ro32(ramfc, 0x00));
+ nv_wr32(dev, 0x3334, nv_ro32(ramfc, 0x04));
+ nv_wr32(dev, 0x3240, nv_ro32(ramfc, 0x08));
+ nv_wr32(dev, 0x3320, nv_ro32(ramfc, 0x0c));
+ nv_wr32(dev, 0x3244, nv_ro32(ramfc, 0x10));
+ nv_wr32(dev, 0x3328, nv_ro32(ramfc, 0x14));
+ nv_wr32(dev, 0x3368, nv_ro32(ramfc, 0x18));
+ nv_wr32(dev, 0x336c, nv_ro32(ramfc, 0x1c));
+ nv_wr32(dev, 0x3370, nv_ro32(ramfc, 0x20));
+ nv_wr32(dev, 0x3374, nv_ro32(ramfc, 0x24));
+ nv_wr32(dev, 0x3378, nv_ro32(ramfc, 0x28));
+ nv_wr32(dev, 0x337c, nv_ro32(ramfc, 0x2c));
+ nv_wr32(dev, 0x3228, nv_ro32(ramfc, 0x30));
+ nv_wr32(dev, 0x3364, nv_ro32(ramfc, 0x34));
+ nv_wr32(dev, 0x32a0, nv_ro32(ramfc, 0x38));
+ nv_wr32(dev, 0x3224, nv_ro32(ramfc, 0x3c));
+ nv_wr32(dev, 0x324c, nv_ro32(ramfc, 0x40));
+ nv_wr32(dev, 0x2044, nv_ro32(ramfc, 0x44));
+ nv_wr32(dev, 0x322c, nv_ro32(ramfc, 0x48));
+ nv_wr32(dev, 0x3234, nv_ro32(ramfc, 0x4c));
+ nv_wr32(dev, 0x3340, nv_ro32(ramfc, 0x50));
+ nv_wr32(dev, 0x3344, nv_ro32(ramfc, 0x54));
+ nv_wr32(dev, 0x3280, nv_ro32(ramfc, 0x58));
+ nv_wr32(dev, 0x3254, nv_ro32(ramfc, 0x5c));
+ nv_wr32(dev, 0x3260, nv_ro32(ramfc, 0x60));
+ nv_wr32(dev, 0x3264, nv_ro32(ramfc, 0x64));
+ nv_wr32(dev, 0x3268, nv_ro32(ramfc, 0x68));
+ nv_wr32(dev, 0x326c, nv_ro32(ramfc, 0x6c));
+ nv_wr32(dev, 0x32e4, nv_ro32(ramfc, 0x70));
+ nv_wr32(dev, 0x3248, nv_ro32(ramfc, 0x74));
+ nv_wr32(dev, 0x2088, nv_ro32(ramfc, 0x78));
+ nv_wr32(dev, 0x2058, nv_ro32(ramfc, 0x7c));
+ nv_wr32(dev, 0x2210, nv_ro32(ramfc, 0x80));
+
+ cnt = nv_ro32(ramfc, 0x84);
for (ptr = 0; ptr < cnt; ptr++) {
nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr),
- nv_ro32(dev, cache, (ptr * 2) + 0));
+ nv_ro32(cache, (ptr * 8) + 0));
nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr),
- nv_ro32(dev, cache, (ptr * 2) + 1));
+ nv_ro32(cache, (ptr * 8) + 4));
}
nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, cnt << 2);
nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
/* guessing that all the 0x34xx regs aren't on NV50 */
if (dev_priv->chipset != 0x50) {
- nv_wr32(dev, 0x340c, nv_ro32(dev, ramfc, 0x88/4));
- nv_wr32(dev, 0x3400, nv_ro32(dev, ramfc, 0x8c/4));
- nv_wr32(dev, 0x3404, nv_ro32(dev, ramfc, 0x90/4));
- nv_wr32(dev, 0x3408, nv_ro32(dev, ramfc, 0x94/4));
- nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4));
+ nv_wr32(dev, 0x340c, nv_ro32(ramfc, 0x88));
+ nv_wr32(dev, 0x3400, nv_ro32(ramfc, 0x8c));
+ nv_wr32(dev, 0x3404, nv_ro32(ramfc, 0x90));
+ nv_wr32(dev, 0x3408, nv_ro32(ramfc, 0x94));
+ nv_wr32(dev, 0x3410, nv_ro32(ramfc, 0x98));
}
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16));
@@ -399,62 +398,63 @@ nv50_fifo_unload_context(struct drm_device *dev)
return -EINVAL;
}
NV_DEBUG(dev, "ch%d\n", chan->id);
- ramfc = chan->ramfc->gpuobj;
- cache = chan->cache->gpuobj;
-
- nv_wo32(dev, ramfc, 0x00/4, nv_rd32(dev, 0x3330));
- nv_wo32(dev, ramfc, 0x04/4, nv_rd32(dev, 0x3334));
- nv_wo32(dev, ramfc, 0x08/4, nv_rd32(dev, 0x3240));
- nv_wo32(dev, ramfc, 0x0c/4, nv_rd32(dev, 0x3320));
- nv_wo32(dev, ramfc, 0x10/4, nv_rd32(dev, 0x3244));
- nv_wo32(dev, ramfc, 0x14/4, nv_rd32(dev, 0x3328));
- nv_wo32(dev, ramfc, 0x18/4, nv_rd32(dev, 0x3368));
- nv_wo32(dev, ramfc, 0x1c/4, nv_rd32(dev, 0x336c));
- nv_wo32(dev, ramfc, 0x20/4, nv_rd32(dev, 0x3370));
- nv_wo32(dev, ramfc, 0x24/4, nv_rd32(dev, 0x3374));
- nv_wo32(dev, ramfc, 0x28/4, nv_rd32(dev, 0x3378));
- nv_wo32(dev, ramfc, 0x2c/4, nv_rd32(dev, 0x337c));
- nv_wo32(dev, ramfc, 0x30/4, nv_rd32(dev, 0x3228));
- nv_wo32(dev, ramfc, 0x34/4, nv_rd32(dev, 0x3364));
- nv_wo32(dev, ramfc, 0x38/4, nv_rd32(dev, 0x32a0));
- nv_wo32(dev, ramfc, 0x3c/4, nv_rd32(dev, 0x3224));
- nv_wo32(dev, ramfc, 0x40/4, nv_rd32(dev, 0x324c));
- nv_wo32(dev, ramfc, 0x44/4, nv_rd32(dev, 0x2044));
- nv_wo32(dev, ramfc, 0x48/4, nv_rd32(dev, 0x322c));
- nv_wo32(dev, ramfc, 0x4c/4, nv_rd32(dev, 0x3234));
- nv_wo32(dev, ramfc, 0x50/4, nv_rd32(dev, 0x3340));
- nv_wo32(dev, ramfc, 0x54/4, nv_rd32(dev, 0x3344));
- nv_wo32(dev, ramfc, 0x58/4, nv_rd32(dev, 0x3280));
- nv_wo32(dev, ramfc, 0x5c/4, nv_rd32(dev, 0x3254));
- nv_wo32(dev, ramfc, 0x60/4, nv_rd32(dev, 0x3260));
- nv_wo32(dev, ramfc, 0x64/4, nv_rd32(dev, 0x3264));
- nv_wo32(dev, ramfc, 0x68/4, nv_rd32(dev, 0x3268));
- nv_wo32(dev, ramfc, 0x6c/4, nv_rd32(dev, 0x326c));
- nv_wo32(dev, ramfc, 0x70/4, nv_rd32(dev, 0x32e4));
- nv_wo32(dev, ramfc, 0x74/4, nv_rd32(dev, 0x3248));
- nv_wo32(dev, ramfc, 0x78/4, nv_rd32(dev, 0x2088));
- nv_wo32(dev, ramfc, 0x7c/4, nv_rd32(dev, 0x2058));
- nv_wo32(dev, ramfc, 0x80/4, nv_rd32(dev, 0x2210));
+ ramfc = chan->ramfc;
+ cache = chan->cache;
+
+ nv_wo32(ramfc, 0x00, nv_rd32(dev, 0x3330));
+ nv_wo32(ramfc, 0x04, nv_rd32(dev, 0x3334));
+ nv_wo32(ramfc, 0x08, nv_rd32(dev, 0x3240));
+ nv_wo32(ramfc, 0x0c, nv_rd32(dev, 0x3320));
+ nv_wo32(ramfc, 0x10, nv_rd32(dev, 0x3244));
+ nv_wo32(ramfc, 0x14, nv_rd32(dev, 0x3328));
+ nv_wo32(ramfc, 0x18, nv_rd32(dev, 0x3368));
+ nv_wo32(ramfc, 0x1c, nv_rd32(dev, 0x336c));
+ nv_wo32(ramfc, 0x20, nv_rd32(dev, 0x3370));
+ nv_wo32(ramfc, 0x24, nv_rd32(dev, 0x3374));
+ nv_wo32(ramfc, 0x28, nv_rd32(dev, 0x3378));
+ nv_wo32(ramfc, 0x2c, nv_rd32(dev, 0x337c));
+ nv_wo32(ramfc, 0x30, nv_rd32(dev, 0x3228));
+ nv_wo32(ramfc, 0x34, nv_rd32(dev, 0x3364));
+ nv_wo32(ramfc, 0x38, nv_rd32(dev, 0x32a0));
+ nv_wo32(ramfc, 0x3c, nv_rd32(dev, 0x3224));
+ nv_wo32(ramfc, 0x40, nv_rd32(dev, 0x324c));
+ nv_wo32(ramfc, 0x44, nv_rd32(dev, 0x2044));
+ nv_wo32(ramfc, 0x48, nv_rd32(dev, 0x322c));
+ nv_wo32(ramfc, 0x4c, nv_rd32(dev, 0x3234));
+ nv_wo32(ramfc, 0x50, nv_rd32(dev, 0x3340));
+ nv_wo32(ramfc, 0x54, nv_rd32(dev, 0x3344));
+ nv_wo32(ramfc, 0x58, nv_rd32(dev, 0x3280));
+ nv_wo32(ramfc, 0x5c, nv_rd32(dev, 0x3254));
+ nv_wo32(ramfc, 0x60, nv_rd32(dev, 0x3260));
+ nv_wo32(ramfc, 0x64, nv_rd32(dev, 0x3264));
+ nv_wo32(ramfc, 0x68, nv_rd32(dev, 0x3268));
+ nv_wo32(ramfc, 0x6c, nv_rd32(dev, 0x326c));
+ nv_wo32(ramfc, 0x70, nv_rd32(dev, 0x32e4));
+ nv_wo32(ramfc, 0x74, nv_rd32(dev, 0x3248));
+ nv_wo32(ramfc, 0x78, nv_rd32(dev, 0x2088));
+ nv_wo32(ramfc, 0x7c, nv_rd32(dev, 0x2058));
+ nv_wo32(ramfc, 0x80, nv_rd32(dev, 0x2210));
put = (nv_rd32(dev, NV03_PFIFO_CACHE1_PUT) & 0x7ff) >> 2;
get = (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) & 0x7ff) >> 2;
ptr = 0;
while (put != get) {
- nv_wo32(dev, cache, ptr++,
- nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get)));
- nv_wo32(dev, cache, ptr++,
- nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get)));
+ nv_wo32(cache, ptr + 0,
+ nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get)));
+ nv_wo32(cache, ptr + 4,
+ nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get)));
get = (get + 1) & 0x1ff;
+ ptr += 8;
}
/* guessing that all the 0x34xx regs aren't on NV50 */
if (dev_priv->chipset != 0x50) {
- nv_wo32(dev, ramfc, 0x84/4, ptr >> 1);
- nv_wo32(dev, ramfc, 0x88/4, nv_rd32(dev, 0x340c));
- nv_wo32(dev, ramfc, 0x8c/4, nv_rd32(dev, 0x3400));
- nv_wo32(dev, ramfc, 0x90/4, nv_rd32(dev, 0x3404));
- nv_wo32(dev, ramfc, 0x94/4, nv_rd32(dev, 0x3408));
- nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410));
+ nv_wo32(ramfc, 0x84, ptr >> 3);
+ nv_wo32(ramfc, 0x88, nv_rd32(dev, 0x340c));
+ nv_wo32(ramfc, 0x8c, nv_rd32(dev, 0x3400));
+ nv_wo32(ramfc, 0x90, nv_rd32(dev, 0x3404));
+ nv_wo32(ramfc, 0x94, nv_rd32(dev, 0x3408));
+ nv_wo32(ramfc, 0x98, nv_rd32(dev, 0x3410));
}
dev_priv->engine.instmem.flush(dev);
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
index 1413028e1580..cbf5ae2f67d4 100644
--- a/drivers/gpu/drm/nouveau/nv50_graph.c
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
@@ -27,7 +27,7 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
-
+#include "nouveau_ramht.h"
#include "nouveau_grctx.h"
static void
@@ -181,7 +181,7 @@ nv50_graph_channel(struct drm_device *dev)
/* Be sure we're not in the middle of a context switch or bad things
* will happen, such as unloading the wrong pgraph context.
*/
- if (!nv_wait(0x400300, 0x00000001, 0x00000000))
+ if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000))
NV_ERROR(dev, "Ctxprog is still running\n");
inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
@@ -192,7 +192,7 @@ nv50_graph_channel(struct drm_device *dev)
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
struct nouveau_channel *chan = dev_priv->fifos[i];
- if (chan && chan->ramin && chan->ramin->instance == inst)
+ if (chan && chan->ramin && chan->ramin->vinst == inst)
return chan;
}
@@ -204,36 +204,34 @@ nv50_graph_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
- struct nouveau_gpuobj *obj;
+ struct nouveau_gpuobj *ramin = chan->ramin;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_grctx ctx = {};
int hdr, ret;
NV_DEBUG(dev, "ch%d\n", chan->id);
- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
- 0x1000, NVOBJ_FLAG_ZERO_ALLOC |
- NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
+ ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
if (ret)
return ret;
- obj = chan->ramin_grctx->gpuobj;
hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20;
- nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
- nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
- pgraph->grctx_size - 1);
- nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance);
- nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0);
- nv_wo32(dev, ramin, (hdr + 0x10)/4, 0);
- nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000);
+ nv_wo32(ramin, hdr + 0x00, 0x00190002);
+ nv_wo32(ramin, hdr + 0x04, chan->ramin_grctx->vinst +
+ pgraph->grctx_size - 1);
+ nv_wo32(ramin, hdr + 0x08, chan->ramin_grctx->vinst);
+ nv_wo32(ramin, hdr + 0x0c, 0);
+ nv_wo32(ramin, hdr + 0x10, 0);
+ nv_wo32(ramin, hdr + 0x14, 0x00010000);
ctx.dev = chan->dev;
ctx.mode = NOUVEAU_GRCTX_VALS;
- ctx.data = obj;
+ ctx.data = chan->ramin_grctx;
nv50_grctx_init(&ctx);
- nv_wo32(dev, obj, 0x00000/4, chan->ramin->instance >> 12);
+ nv_wo32(chan->ramin_grctx, 0x00000, chan->ramin->vinst >> 12);
dev_priv->engine.instmem.flush(dev);
return 0;
@@ -248,14 +246,14 @@ nv50_graph_destroy_context(struct nouveau_channel *chan)
NV_DEBUG(dev, "ch%d\n", chan->id);
- if (!chan->ramin || !chan->ramin->gpuobj)
+ if (!chan->ramin)
return;
for (i = hdr; i < hdr + 24; i += 4)
- nv_wo32(dev, chan->ramin->gpuobj, i/4, 0);
+ nv_wo32(chan->ramin, i, 0);
dev_priv->engine.instmem.flush(dev);
- nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
+ nouveau_gpuobj_ref(NULL, &chan->ramin_grctx);
}
static int
@@ -282,7 +280,7 @@ nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst)
int
nv50_graph_load_context(struct nouveau_channel *chan)
{
- uint32_t inst = chan->ramin->instance >> 12;
+ uint32_t inst = chan->ramin->vinst >> 12;
NV_DEBUG(chan->dev, "ch%d\n", chan->id);
return nv50_graph_do_load_context(chan->dev, inst);
@@ -327,15 +325,16 @@ static int
nv50_graph_nvsw_dma_vblsem(struct nouveau_channel *chan, int grclass,
int mthd, uint32_t data)
{
- struct nouveau_gpuobj_ref *ref = NULL;
+ struct nouveau_gpuobj *gpuobj;
- if (nouveau_gpuobj_ref_find(chan, data, &ref))
+ gpuobj = nouveau_ramht_find(chan, data);
+ if (!gpuobj)
return -ENOENT;
- if (nouveau_notifier_offset(ref->gpuobj, NULL))
+ if (nouveau_notifier_offset(gpuobj, NULL))
return -EINVAL;
- chan->nvsw.vblsem = ref->gpuobj;
+ chan->nvsw.vblsem = gpuobj;
chan->nvsw.vblsem_offset = ~0;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c
index 42a8fb20c1e6..336aab2a24a6 100644
--- a/drivers/gpu/drm/nouveau/nv50_grctx.c
+++ b/drivers/gpu/drm/nouveau/nv50_grctx.c
@@ -103,6 +103,9 @@
#include "nouveau_drv.h"
#include "nouveau_grctx.h"
+#define IS_NVA3F(x) (((x) > 0xa0 && (x) < 0xaa) || (x) == 0xaf)
+#define IS_NVAAF(x) ((x) >= 0xaa && (x) <= 0xac)
+
/*
* This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's
* the GPU itself that does context-switching, but it needs a special
@@ -182,6 +185,7 @@ nv50_grctx_init(struct nouveau_grctx *ctx)
case 0xa8:
case 0xaa:
case 0xac:
+ case 0xaf:
break;
default:
NV_ERROR(ctx->dev, "I don't know how to make a ctxprog for "
@@ -268,6 +272,9 @@ nv50_grctx_init(struct nouveau_grctx *ctx)
*/
static void
+nv50_graph_construct_mmio_ddata(struct nouveau_grctx *ctx);
+
+static void
nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
@@ -286,7 +293,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
gr_def(ctx, 0x400840, 0xffe806a8);
}
gr_def(ctx, 0x400844, 0x00000002);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+ if (IS_NVA3F(dev_priv->chipset))
gr_def(ctx, 0x400894, 0x00001000);
gr_def(ctx, 0x4008e8, 0x00000003);
gr_def(ctx, 0x4008ec, 0x00001000);
@@ -299,13 +306,15 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
if (dev_priv->chipset >= 0xa0)
cp_ctx(ctx, 0x400b00, 0x1);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+ if (IS_NVA3F(dev_priv->chipset)) {
cp_ctx(ctx, 0x400b10, 0x1);
gr_def(ctx, 0x400b10, 0x0001629d);
cp_ctx(ctx, 0x400b20, 0x1);
gr_def(ctx, 0x400b20, 0x0001629d);
}
+ nv50_graph_construct_mmio_ddata(ctx);
+
/* 0C00: VFETCH */
cp_ctx(ctx, 0x400c08, 0x2);
gr_def(ctx, 0x400c08, 0x0000fe0c);
@@ -314,7 +323,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
if (dev_priv->chipset < 0xa0) {
cp_ctx(ctx, 0x401008, 0x4);
gr_def(ctx, 0x401014, 0x00001000);
- } else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) {
+ } else if (!IS_NVA3F(dev_priv->chipset)) {
cp_ctx(ctx, 0x401008, 0x5);
gr_def(ctx, 0x401018, 0x00001000);
} else {
@@ -368,10 +377,13 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
case 0xa3:
case 0xa5:
case 0xa8:
+ case 0xaf:
gr_def(ctx, 0x401c00, 0x142500df);
break;
}
+ /* 2000 */
+
/* 2400 */
cp_ctx(ctx, 0x402400, 0x1);
if (dev_priv->chipset == 0x50)
@@ -380,12 +392,12 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
cp_ctx(ctx, 0x402408, 0x2);
gr_def(ctx, 0x402408, 0x00000600);
- /* 2800 */
+ /* 2800: CSCHED */
cp_ctx(ctx, 0x402800, 0x1);
if (dev_priv->chipset == 0x50)
gr_def(ctx, 0x402800, 0x00000006);
- /* 2C00 */
+ /* 2C00: ZCULL */
cp_ctx(ctx, 0x402c08, 0x6);
if (dev_priv->chipset != 0x50)
gr_def(ctx, 0x402c14, 0x01000000);
@@ -396,23 +408,23 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
cp_ctx(ctx, 0x402ca0, 0x2);
if (dev_priv->chipset < 0xa0)
gr_def(ctx, 0x402ca0, 0x00000400);
- else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa)
+ else if (!IS_NVA3F(dev_priv->chipset))
gr_def(ctx, 0x402ca0, 0x00000800);
else
gr_def(ctx, 0x402ca0, 0x00000400);
cp_ctx(ctx, 0x402cac, 0x4);
- /* 3000 */
+ /* 3000: ENG2D */
cp_ctx(ctx, 0x403004, 0x1);
gr_def(ctx, 0x403004, 0x00000001);
- /* 3404 */
+ /* 3400 */
if (dev_priv->chipset >= 0xa0) {
cp_ctx(ctx, 0x403404, 0x1);
gr_def(ctx, 0x403404, 0x00000001);
}
- /* 5000 */
+ /* 5000: CCACHE */
cp_ctx(ctx, 0x405000, 0x1);
switch (dev_priv->chipset) {
case 0x50:
@@ -425,6 +437,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
case 0xa8:
case 0xaa:
case 0xac:
+ case 0xaf:
gr_def(ctx, 0x405000, 0x000e0080);
break;
case 0x86:
@@ -441,210 +454,6 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
cp_ctx(ctx, 0x405024, 0x1);
cp_ctx(ctx, 0x40502c, 0x1);
- /* 5400 or maybe 4800 */
- if (dev_priv->chipset == 0x50) {
- offset = 0x405400;
- cp_ctx(ctx, 0x405400, 0xea);
- } else if (dev_priv->chipset < 0x94) {
- offset = 0x405400;
- cp_ctx(ctx, 0x405400, 0xcb);
- } else if (dev_priv->chipset < 0xa0) {
- offset = 0x405400;
- cp_ctx(ctx, 0x405400, 0xcc);
- } else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- offset = 0x404800;
- cp_ctx(ctx, 0x404800, 0xda);
- } else {
- offset = 0x405400;
- cp_ctx(ctx, 0x405400, 0xd4);
- }
- gr_def(ctx, offset + 0x0c, 0x00000002);
- gr_def(ctx, offset + 0x10, 0x00000001);
- if (dev_priv->chipset >= 0x94)
- offset += 4;
- gr_def(ctx, offset + 0x1c, 0x00000001);
- gr_def(ctx, offset + 0x20, 0x00000100);
- gr_def(ctx, offset + 0x38, 0x00000002);
- gr_def(ctx, offset + 0x3c, 0x00000001);
- gr_def(ctx, offset + 0x40, 0x00000001);
- gr_def(ctx, offset + 0x50, 0x00000001);
- gr_def(ctx, offset + 0x54, 0x003fffff);
- gr_def(ctx, offset + 0x58, 0x00001fff);
- gr_def(ctx, offset + 0x60, 0x00000001);
- gr_def(ctx, offset + 0x64, 0x00000001);
- gr_def(ctx, offset + 0x6c, 0x00000001);
- gr_def(ctx, offset + 0x70, 0x00000001);
- gr_def(ctx, offset + 0x74, 0x00000001);
- gr_def(ctx, offset + 0x78, 0x00000004);
- gr_def(ctx, offset + 0x7c, 0x00000001);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- offset += 4;
- gr_def(ctx, offset + 0x80, 0x00000001);
- gr_def(ctx, offset + 0x84, 0x00000001);
- gr_def(ctx, offset + 0x88, 0x00000007);
- gr_def(ctx, offset + 0x8c, 0x00000001);
- gr_def(ctx, offset + 0x90, 0x00000007);
- gr_def(ctx, offset + 0x94, 0x00000001);
- gr_def(ctx, offset + 0x98, 0x00000001);
- gr_def(ctx, offset + 0x9c, 0x00000001);
- if (dev_priv->chipset == 0x50) {
- gr_def(ctx, offset + 0xb0, 0x00000001);
- gr_def(ctx, offset + 0xb4, 0x00000001);
- gr_def(ctx, offset + 0xbc, 0x00000001);
- gr_def(ctx, offset + 0xc0, 0x0000000a);
- gr_def(ctx, offset + 0xd0, 0x00000040);
- gr_def(ctx, offset + 0xd8, 0x00000002);
- gr_def(ctx, offset + 0xdc, 0x00000100);
- gr_def(ctx, offset + 0xe0, 0x00000001);
- gr_def(ctx, offset + 0xe4, 0x00000100);
- gr_def(ctx, offset + 0x100, 0x00000001);
- gr_def(ctx, offset + 0x124, 0x00000004);
- gr_def(ctx, offset + 0x13c, 0x00000001);
- gr_def(ctx, offset + 0x140, 0x00000100);
- gr_def(ctx, offset + 0x148, 0x00000001);
- gr_def(ctx, offset + 0x154, 0x00000100);
- gr_def(ctx, offset + 0x158, 0x00000001);
- gr_def(ctx, offset + 0x15c, 0x00000100);
- gr_def(ctx, offset + 0x164, 0x00000001);
- gr_def(ctx, offset + 0x170, 0x00000100);
- gr_def(ctx, offset + 0x174, 0x00000001);
- gr_def(ctx, offset + 0x17c, 0x00000001);
- gr_def(ctx, offset + 0x188, 0x00000002);
- gr_def(ctx, offset + 0x190, 0x00000001);
- gr_def(ctx, offset + 0x198, 0x00000001);
- gr_def(ctx, offset + 0x1ac, 0x00000003);
- offset += 0xd0;
- } else {
- gr_def(ctx, offset + 0xb0, 0x00000001);
- gr_def(ctx, offset + 0xb4, 0x00000100);
- gr_def(ctx, offset + 0xbc, 0x00000001);
- gr_def(ctx, offset + 0xc8, 0x00000100);
- gr_def(ctx, offset + 0xcc, 0x00000001);
- gr_def(ctx, offset + 0xd0, 0x00000100);
- gr_def(ctx, offset + 0xd8, 0x00000001);
- gr_def(ctx, offset + 0xe4, 0x00000100);
- }
- gr_def(ctx, offset + 0xf8, 0x00000004);
- gr_def(ctx, offset + 0xfc, 0x00000070);
- gr_def(ctx, offset + 0x100, 0x00000080);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- offset += 4;
- gr_def(ctx, offset + 0x114, 0x0000000c);
- if (dev_priv->chipset == 0x50)
- offset -= 4;
- gr_def(ctx, offset + 0x11c, 0x00000008);
- gr_def(ctx, offset + 0x120, 0x00000014);
- if (dev_priv->chipset == 0x50) {
- gr_def(ctx, offset + 0x124, 0x00000026);
- offset -= 0x18;
- } else {
- gr_def(ctx, offset + 0x128, 0x00000029);
- gr_def(ctx, offset + 0x12c, 0x00000027);
- gr_def(ctx, offset + 0x130, 0x00000026);
- gr_def(ctx, offset + 0x134, 0x00000008);
- gr_def(ctx, offset + 0x138, 0x00000004);
- gr_def(ctx, offset + 0x13c, 0x00000027);
- }
- gr_def(ctx, offset + 0x148, 0x00000001);
- gr_def(ctx, offset + 0x14c, 0x00000002);
- gr_def(ctx, offset + 0x150, 0x00000003);
- gr_def(ctx, offset + 0x154, 0x00000004);
- gr_def(ctx, offset + 0x158, 0x00000005);
- gr_def(ctx, offset + 0x15c, 0x00000006);
- gr_def(ctx, offset + 0x160, 0x00000007);
- gr_def(ctx, offset + 0x164, 0x00000001);
- gr_def(ctx, offset + 0x1a8, 0x000000cf);
- if (dev_priv->chipset == 0x50)
- offset -= 4;
- gr_def(ctx, offset + 0x1d8, 0x00000080);
- gr_def(ctx, offset + 0x1dc, 0x00000004);
- gr_def(ctx, offset + 0x1e0, 0x00000004);
- if (dev_priv->chipset == 0x50)
- offset -= 4;
- else
- gr_def(ctx, offset + 0x1e4, 0x00000003);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- gr_def(ctx, offset + 0x1ec, 0x00000003);
- offset += 8;
- }
- gr_def(ctx, offset + 0x1e8, 0x00000001);
- if (dev_priv->chipset == 0x50)
- offset -= 4;
- gr_def(ctx, offset + 0x1f4, 0x00000012);
- gr_def(ctx, offset + 0x1f8, 0x00000010);
- gr_def(ctx, offset + 0x1fc, 0x0000000c);
- gr_def(ctx, offset + 0x200, 0x00000001);
- gr_def(ctx, offset + 0x210, 0x00000004);
- gr_def(ctx, offset + 0x214, 0x00000002);
- gr_def(ctx, offset + 0x218, 0x00000004);
- if (dev_priv->chipset >= 0xa0)
- offset += 4;
- gr_def(ctx, offset + 0x224, 0x003fffff);
- gr_def(ctx, offset + 0x228, 0x00001fff);
- if (dev_priv->chipset == 0x50)
- offset -= 0x20;
- else if (dev_priv->chipset >= 0xa0) {
- gr_def(ctx, offset + 0x250, 0x00000001);
- gr_def(ctx, offset + 0x254, 0x00000001);
- gr_def(ctx, offset + 0x258, 0x00000002);
- offset += 0x10;
- }
- gr_def(ctx, offset + 0x250, 0x00000004);
- gr_def(ctx, offset + 0x254, 0x00000014);
- gr_def(ctx, offset + 0x258, 0x00000001);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- offset += 4;
- gr_def(ctx, offset + 0x264, 0x00000002);
- if (dev_priv->chipset >= 0xa0)
- offset += 8;
- gr_def(ctx, offset + 0x270, 0x00000001);
- gr_def(ctx, offset + 0x278, 0x00000002);
- gr_def(ctx, offset + 0x27c, 0x00001000);
- if (dev_priv->chipset == 0x50)
- offset -= 0xc;
- else {
- gr_def(ctx, offset + 0x280, 0x00000e00);
- gr_def(ctx, offset + 0x284, 0x00001000);
- gr_def(ctx, offset + 0x288, 0x00001e00);
- }
- gr_def(ctx, offset + 0x290, 0x00000001);
- gr_def(ctx, offset + 0x294, 0x00000001);
- gr_def(ctx, offset + 0x298, 0x00000001);
- gr_def(ctx, offset + 0x29c, 0x00000001);
- gr_def(ctx, offset + 0x2a0, 0x00000001);
- gr_def(ctx, offset + 0x2b0, 0x00000200);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- gr_def(ctx, offset + 0x2b4, 0x00000200);
- offset += 4;
- }
- if (dev_priv->chipset < 0xa0) {
- gr_def(ctx, offset + 0x2b8, 0x00000001);
- gr_def(ctx, offset + 0x2bc, 0x00000070);
- gr_def(ctx, offset + 0x2c0, 0x00000080);
- gr_def(ctx, offset + 0x2cc, 0x00000001);
- gr_def(ctx, offset + 0x2d0, 0x00000070);
- gr_def(ctx, offset + 0x2d4, 0x00000080);
- } else {
- gr_def(ctx, offset + 0x2b8, 0x00000001);
- gr_def(ctx, offset + 0x2bc, 0x000000f0);
- gr_def(ctx, offset + 0x2c0, 0x000000ff);
- gr_def(ctx, offset + 0x2cc, 0x00000001);
- gr_def(ctx, offset + 0x2d0, 0x000000f0);
- gr_def(ctx, offset + 0x2d4, 0x000000ff);
- gr_def(ctx, offset + 0x2dc, 0x00000009);
- offset += 4;
- }
- gr_def(ctx, offset + 0x2e4, 0x00000001);
- gr_def(ctx, offset + 0x2e8, 0x000000cf);
- gr_def(ctx, offset + 0x2f0, 0x00000001);
- gr_def(ctx, offset + 0x300, 0x000000cf);
- gr_def(ctx, offset + 0x308, 0x00000002);
- gr_def(ctx, offset + 0x310, 0x00000001);
- gr_def(ctx, offset + 0x318, 0x00000001);
- gr_def(ctx, offset + 0x320, 0x000000cf);
- gr_def(ctx, offset + 0x324, 0x000000cf);
- gr_def(ctx, offset + 0x328, 0x00000001);
-
/* 6000? */
if (dev_priv->chipset == 0x50)
cp_ctx(ctx, 0x4063e0, 0x1);
@@ -661,7 +470,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
gr_def(ctx, 0x406818, 0x00000f80);
else
gr_def(ctx, 0x406818, 0x00001f80);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+ if (IS_NVA3F(dev_priv->chipset))
gr_def(ctx, 0x40681c, 0x00000030);
cp_ctx(ctx, 0x406830, 0x3);
}
@@ -706,7 +515,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
if (dev_priv->chipset < 0xa0)
cp_ctx(ctx, 0x407094 + (i<<8), 1);
- else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
+ else if (!IS_NVA3F(dev_priv->chipset))
cp_ctx(ctx, 0x407094 + (i<<8), 3);
else {
cp_ctx(ctx, 0x407094 + (i<<8), 4);
@@ -799,6 +608,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
case 0xa8:
case 0xaa:
case 0xac:
+ case 0xaf:
gr_def(ctx, offset + 0x1c, 0x300c0000);
break;
}
@@ -825,7 +635,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
gr_def(ctx, base + 0x304, 0x00007070);
else if (dev_priv->chipset < 0xa0)
gr_def(ctx, base + 0x304, 0x00027070);
- else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
+ else if (!IS_NVA3F(dev_priv->chipset))
gr_def(ctx, base + 0x304, 0x01127070);
else
gr_def(ctx, base + 0x304, 0x05127070);
@@ -849,7 +659,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
if (dev_priv->chipset < 0xa0) {
cp_ctx(ctx, base + 0x340, 9);
offset = base + 0x340;
- } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
+ } else if (!IS_NVA3F(dev_priv->chipset)) {
cp_ctx(ctx, base + 0x33c, 0xb);
offset = base + 0x344;
} else {
@@ -880,7 +690,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
gr_def(ctx, offset + 0x0, 0x000001f0);
gr_def(ctx, offset + 0x4, 0x00000001);
gr_def(ctx, offset + 0x8, 0x00000003);
- if (dev_priv->chipset == 0x50 || dev_priv->chipset >= 0xaa)
+ if (dev_priv->chipset == 0x50 || IS_NVAAF(dev_priv->chipset))
gr_def(ctx, offset + 0xc, 0x00008000);
gr_def(ctx, offset + 0x14, 0x00039e00);
cp_ctx(ctx, offset + 0x1c, 2);
@@ -892,7 +702,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
if (dev_priv->chipset >= 0xa0) {
cp_ctx(ctx, base + 0x54c, 2);
- if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
+ if (!IS_NVA3F(dev_priv->chipset))
gr_def(ctx, base + 0x54c, 0x003fe006);
else
gr_def(ctx, base + 0x54c, 0x003fe007);
@@ -948,6 +758,336 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
}
}
+static void
+dd_emit(struct nouveau_grctx *ctx, int num, uint32_t val) {
+ int i;
+ if (val && ctx->mode == NOUVEAU_GRCTX_VALS)
+ for (i = 0; i < num; i++)
+ nv_wo32(ctx->data, 4 * (ctx->ctxvals_pos + i), val);
+ ctx->ctxvals_pos += num;
+}
+
+static void
+nv50_graph_construct_mmio_ddata(struct nouveau_grctx *ctx)
+{
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ int base, num;
+ base = ctx->ctxvals_pos;
+
+ /* tesla state */
+ dd_emit(ctx, 1, 0); /* 00000001 UNK0F90 */
+ dd_emit(ctx, 1, 0); /* 00000001 UNK135C */
+
+ /* SRC_TIC state */
+ dd_emit(ctx, 1, 0); /* 00000007 SRC_TILE_MODE_Z */
+ dd_emit(ctx, 1, 2); /* 00000007 SRC_TILE_MODE_Y */
+ dd_emit(ctx, 1, 1); /* 00000001 SRC_LINEAR #1 */
+ dd_emit(ctx, 1, 0); /* 000000ff SRC_ADDRESS_HIGH */
+ dd_emit(ctx, 1, 0); /* 00000001 SRC_SRGB */
+ if (dev_priv->chipset >= 0x94)
+ dd_emit(ctx, 1, 0); /* 00000003 eng2d UNK0258 */
+ dd_emit(ctx, 1, 1); /* 00000fff SRC_DEPTH */
+ dd_emit(ctx, 1, 0x100); /* 0000ffff SRC_HEIGHT */
+
+ /* turing state */
+ dd_emit(ctx, 1, 0); /* 0000000f TEXTURES_LOG2 */
+ dd_emit(ctx, 1, 0); /* 0000000f SAMPLERS_LOG2 */
+ dd_emit(ctx, 1, 0); /* 000000ff CB_DEF_ADDRESS_HIGH */
+ dd_emit(ctx, 1, 0); /* ffffffff CB_DEF_ADDRESS_LOW */
+ dd_emit(ctx, 1, 0); /* ffffffff SHARED_SIZE */
+ dd_emit(ctx, 1, 2); /* ffffffff REG_MODE */
+ dd_emit(ctx, 1, 1); /* 0000ffff BLOCK_ALLOC_THREADS */
+ dd_emit(ctx, 1, 1); /* 00000001 LANES32 */
+ dd_emit(ctx, 1, 0); /* 000000ff UNK370 */
+ dd_emit(ctx, 1, 0); /* 000000ff USER_PARAM_UNK */
+ dd_emit(ctx, 1, 0); /* 000000ff USER_PARAM_COUNT */
+ dd_emit(ctx, 1, 1); /* 000000ff UNK384 bits 8-15 */
+ dd_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */
+ dd_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */
+ dd_emit(ctx, 1, 0); /* 0000ffff CB_ADDR_INDEX */
+ dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_X */
+ dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_XMY */
+ dd_emit(ctx, 1, 0); /* 00000001 BLOCKDIM_XMY_OVERFLOW */
+ dd_emit(ctx, 1, 1); /* 0003ffff BLOCKDIM_XMYMZ */
+ dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_Y */
+ dd_emit(ctx, 1, 1); /* 0000007f BLOCKDIM_Z */
+ dd_emit(ctx, 1, 4); /* 000000ff CP_REG_ALLOC_TEMP */
+ dd_emit(ctx, 1, 1); /* 00000001 BLOCKDIM_DIRTY */
+ if (IS_NVA3F(dev_priv->chipset))
+ dd_emit(ctx, 1, 0); /* 00000003 UNK03E8 */
+ dd_emit(ctx, 1, 1); /* 0000007f BLOCK_ALLOC_HALFWARPS */
+ dd_emit(ctx, 1, 1); /* 00000007 LOCAL_WARPS_NO_CLAMP */
+ dd_emit(ctx, 1, 7); /* 00000007 LOCAL_WARPS_LOG_ALLOC */
+ dd_emit(ctx, 1, 1); /* 00000007 STACK_WARPS_NO_CLAMP */
+ dd_emit(ctx, 1, 7); /* 00000007 STACK_WARPS_LOG_ALLOC */
+ dd_emit(ctx, 1, 1); /* 00001fff BLOCK_ALLOC_REGSLOTS_PACKED */
+ dd_emit(ctx, 1, 1); /* 00001fff BLOCK_ALLOC_REGSLOTS_STRIDED */
+ dd_emit(ctx, 1, 1); /* 000007ff BLOCK_ALLOC_THREADS */
+
+ /* compat 2d state */
+ if (dev_priv->chipset == 0x50) {
+ dd_emit(ctx, 4, 0); /* 0000ffff clip X, Y, W, H */
+
+ dd_emit(ctx, 1, 1); /* ffffffff chroma COLOR_FORMAT */
+
+ dd_emit(ctx, 1, 1); /* ffffffff pattern COLOR_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff pattern SHAPE */
+ dd_emit(ctx, 1, 1); /* ffffffff pattern PATTERN_SELECT */
+
+ dd_emit(ctx, 1, 0xa); /* ffffffff surf2d SRC_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff surf2d DMA_SRC */
+ dd_emit(ctx, 1, 0); /* 000000ff surf2d SRC_ADDRESS_HIGH */
+ dd_emit(ctx, 1, 0); /* ffffffff surf2d SRC_ADDRESS_LOW */
+ dd_emit(ctx, 1, 0x40); /* 0000ffff surf2d SRC_PITCH */
+ dd_emit(ctx, 1, 0); /* 0000000f surf2d SRC_TILE_MODE_Z */
+ dd_emit(ctx, 1, 2); /* 0000000f surf2d SRC_TILE_MODE_Y */
+ dd_emit(ctx, 1, 0x100); /* ffffffff surf2d SRC_HEIGHT */
+ dd_emit(ctx, 1, 1); /* 00000001 surf2d SRC_LINEAR */
+ dd_emit(ctx, 1, 0x100); /* ffffffff surf2d SRC_WIDTH */
+
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_B_X */
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_B_Y */
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_C_X */
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_C_Y */
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_D_X */
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_D_Y */
+ dd_emit(ctx, 1, 1); /* ffffffff gdirect COLOR_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff gdirect OPERATION */
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect POINT_X */
+ dd_emit(ctx, 1, 0); /* 0000ffff gdirect POINT_Y */
+
+ dd_emit(ctx, 1, 0); /* 0000ffff blit SRC_Y */
+ dd_emit(ctx, 1, 0); /* ffffffff blit OPERATION */
+
+ dd_emit(ctx, 1, 0); /* ffffffff ifc OPERATION */
+
+ dd_emit(ctx, 1, 0); /* ffffffff iifc INDEX_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff iifc LUT_OFFSET */
+ dd_emit(ctx, 1, 4); /* ffffffff iifc COLOR_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff iifc OPERATION */
+ }
+
+ /* m2mf state */
+ dd_emit(ctx, 1, 0); /* ffffffff m2mf LINE_COUNT */
+ dd_emit(ctx, 1, 0); /* ffffffff m2mf LINE_LENGTH_IN */
+ dd_emit(ctx, 2, 0); /* ffffffff m2mf OFFSET_IN, OFFSET_OUT */
+ dd_emit(ctx, 1, 1); /* ffffffff m2mf TILING_DEPTH_OUT */
+ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_HEIGHT_OUT */
+ dd_emit(ctx, 1, 0); /* ffffffff m2mf TILING_POSITION_OUT_Z */
+ dd_emit(ctx, 1, 1); /* 00000001 m2mf LINEAR_OUT */
+ dd_emit(ctx, 2, 0); /* 0000ffff m2mf TILING_POSITION_OUT_X, Y */
+ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_PITCH_OUT */
+ dd_emit(ctx, 1, 1); /* ffffffff m2mf TILING_DEPTH_IN */
+ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_HEIGHT_IN */
+ dd_emit(ctx, 1, 0); /* ffffffff m2mf TILING_POSITION_IN_Z */
+ dd_emit(ctx, 1, 1); /* 00000001 m2mf LINEAR_IN */
+ dd_emit(ctx, 2, 0); /* 0000ffff m2mf TILING_POSITION_IN_X, Y */
+ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_PITCH_IN */
+
+ /* more compat 2d state */
+ if (dev_priv->chipset == 0x50) {
+ dd_emit(ctx, 1, 1); /* ffffffff line COLOR_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff line OPERATION */
+
+ dd_emit(ctx, 1, 1); /* ffffffff triangle COLOR_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff triangle OPERATION */
+
+ dd_emit(ctx, 1, 0); /* 0000000f sifm TILE_MODE_Z */
+ dd_emit(ctx, 1, 2); /* 0000000f sifm TILE_MODE_Y */
+ dd_emit(ctx, 1, 0); /* 000000ff sifm FORMAT_FILTER */
+ dd_emit(ctx, 1, 1); /* 000000ff sifm FORMAT_ORIGIN */
+ dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_PITCH */
+ dd_emit(ctx, 1, 1); /* 00000001 sifm SRC_LINEAR */
+ dd_emit(ctx, 1, 0); /* 000000ff sifm SRC_OFFSET_HIGH */
+ dd_emit(ctx, 1, 0); /* ffffffff sifm SRC_OFFSET */
+ dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_HEIGHT */
+ dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_WIDTH */
+ dd_emit(ctx, 1, 3); /* ffffffff sifm COLOR_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff sifm OPERATION */
+
+ dd_emit(ctx, 1, 0); /* ffffffff sifc OPERATION */
+ }
+
+ /* tesla state */
+ dd_emit(ctx, 1, 0); /* 0000000f GP_TEXTURES_LOG2 */
+ dd_emit(ctx, 1, 0); /* 0000000f GP_SAMPLERS_LOG2 */
+ dd_emit(ctx, 1, 0); /* 000000ff */
+ dd_emit(ctx, 1, 0); /* ffffffff */
+ dd_emit(ctx, 1, 4); /* 000000ff UNK12B0_0 */
+ dd_emit(ctx, 1, 0x70); /* 000000ff UNK12B0_1 */
+ dd_emit(ctx, 1, 0x80); /* 000000ff UNK12B0_3 */
+ dd_emit(ctx, 1, 0); /* 000000ff UNK12B0_2 */
+ dd_emit(ctx, 1, 0); /* 0000000f FP_TEXTURES_LOG2 */
+ dd_emit(ctx, 1, 0); /* 0000000f FP_SAMPLERS_LOG2 */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ dd_emit(ctx, 1, 0); /* ffffffff */
+ dd_emit(ctx, 1, 0); /* 0000007f MULTISAMPLE_SAMPLES_LOG2 */
+ } else {
+ dd_emit(ctx, 1, 0); /* 0000000f MULTISAMPLE_SAMPLES_LOG2 */
+ }
+ dd_emit(ctx, 1, 0xc); /* 000000ff SEMANTIC_COLOR.BFC0_ID */
+ if (dev_priv->chipset != 0x50)
+ dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_COLOR.CLMP_EN */
+ dd_emit(ctx, 1, 8); /* 000000ff SEMANTIC_COLOR.COLR_NR */
+ dd_emit(ctx, 1, 0x14); /* 000000ff SEMANTIC_COLOR.FFC0_ID */
+ if (dev_priv->chipset == 0x50) {
+ dd_emit(ctx, 1, 0); /* 000000ff SEMANTIC_LAYER */
+ dd_emit(ctx, 1, 0); /* 00000001 */
+ } else {
+ dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_PTSZ.ENABLE */
+ dd_emit(ctx, 1, 0x29); /* 000000ff SEMANTIC_PTSZ.PTSZ_ID */
+ dd_emit(ctx, 1, 0x27); /* 000000ff SEMANTIC_PRIM */
+ dd_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */
+ dd_emit(ctx, 1, 8); /* 0000000f SMENATIC_CLIP.CLIP_HIGH */
+ dd_emit(ctx, 1, 4); /* 000000ff SEMANTIC_CLIP.CLIP_LO */
+ dd_emit(ctx, 1, 0x27); /* 000000ff UNK0FD4 */
+ dd_emit(ctx, 1, 0); /* 00000001 UNK1900 */
+ }
+ dd_emit(ctx, 1, 0); /* 00000007 RT_CONTROL_MAP0 */
+ dd_emit(ctx, 1, 1); /* 00000007 RT_CONTROL_MAP1 */
+ dd_emit(ctx, 1, 2); /* 00000007 RT_CONTROL_MAP2 */
+ dd_emit(ctx, 1, 3); /* 00000007 RT_CONTROL_MAP3 */
+ dd_emit(ctx, 1, 4); /* 00000007 RT_CONTROL_MAP4 */
+ dd_emit(ctx, 1, 5); /* 00000007 RT_CONTROL_MAP5 */
+ dd_emit(ctx, 1, 6); /* 00000007 RT_CONTROL_MAP6 */
+ dd_emit(ctx, 1, 7); /* 00000007 RT_CONTROL_MAP7 */
+ dd_emit(ctx, 1, 1); /* 0000000f RT_CONTROL_COUNT */
+ dd_emit(ctx, 8, 0); /* 00000001 RT_HORIZ_UNK */
+ dd_emit(ctx, 8, 0); /* ffffffff RT_ADDRESS_LOW */
+ dd_emit(ctx, 1, 0xcf); /* 000000ff RT_FORMAT */
+ dd_emit(ctx, 7, 0); /* 000000ff RT_FORMAT */
+ if (dev_priv->chipset != 0x50)
+ dd_emit(ctx, 3, 0); /* 1, 1, 1 */
+ else
+ dd_emit(ctx, 2, 0); /* 1, 1 */
+ dd_emit(ctx, 1, 0); /* ffffffff GP_ENABLE */
+ dd_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT*/
+ dd_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */
+ dd_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ dd_emit(ctx, 1, 3); /* 00000003 */
+ dd_emit(ctx, 1, 0); /* 00000001 UNK1418. Alone. */
+ }
+ if (dev_priv->chipset != 0x50)
+ dd_emit(ctx, 1, 3); /* 00000003 UNK15AC */
+ dd_emit(ctx, 1, 1); /* ffffffff RASTERIZE_ENABLE */
+ dd_emit(ctx, 1, 0); /* 00000001 FP_CONTROL.EXPORTS_Z */
+ if (dev_priv->chipset != 0x50)
+ dd_emit(ctx, 1, 0); /* 00000001 FP_CONTROL.MULTIPLE_RESULTS */
+ dd_emit(ctx, 1, 0x12); /* 000000ff FP_INTERPOLANT_CTRL.COUNT */
+ dd_emit(ctx, 1, 0x10); /* 000000ff FP_INTERPOLANT_CTRL.COUNT_NONFLAT */
+ dd_emit(ctx, 1, 0xc); /* 000000ff FP_INTERPOLANT_CTRL.OFFSET */
+ dd_emit(ctx, 1, 1); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.W */
+ dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.X */
+ dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.Y */
+ dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.Z */
+ dd_emit(ctx, 1, 4); /* 000000ff FP_RESULT_COUNT */
+ dd_emit(ctx, 1, 2); /* ffffffff REG_MODE */
+ dd_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */
+ if (dev_priv->chipset >= 0xa0)
+ dd_emit(ctx, 1, 0); /* ffffffff */
+ dd_emit(ctx, 1, 0); /* 00000001 GP_BUILTIN_RESULT_EN.LAYER_IDX */
+ dd_emit(ctx, 1, 0); /* ffffffff STRMOUT_ENABLE */
+ dd_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */
+ dd_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */
+ dd_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE*/
+ if (dev_priv->chipset != 0x50)
+ dd_emit(ctx, 8, 0); /* 00000001 */
+ if (dev_priv->chipset >= 0xa0) {
+ dd_emit(ctx, 1, 1); /* 00000007 VTX_ATTR_DEFINE.COMP */
+ dd_emit(ctx, 1, 1); /* 00000007 VTX_ATTR_DEFINE.SIZE */
+ dd_emit(ctx, 1, 2); /* 00000007 VTX_ATTR_DEFINE.TYPE */
+ dd_emit(ctx, 1, 0); /* 000000ff VTX_ATTR_DEFINE.ATTR */
+ }
+ dd_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ dd_emit(ctx, 1, 0x14); /* 0000001f ZETA_FORMAT */
+ dd_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ dd_emit(ctx, 1, 0); /* 0000000f VP_TEXTURES_LOG2 */
+ dd_emit(ctx, 1, 0); /* 0000000f VP_SAMPLERS_LOG2 */
+ if (IS_NVA3F(dev_priv->chipset))
+ dd_emit(ctx, 1, 0); /* 00000001 */
+ dd_emit(ctx, 1, 2); /* 00000003 POLYGON_MODE_BACK */
+ if (dev_priv->chipset >= 0xa0)
+ dd_emit(ctx, 1, 0); /* 00000003 VTX_ATTR_DEFINE.SIZE - 1 */
+ dd_emit(ctx, 1, 0); /* 0000ffff CB_ADDR_INDEX */
+ if (dev_priv->chipset >= 0xa0)
+ dd_emit(ctx, 1, 0); /* 00000003 */
+ dd_emit(ctx, 1, 0); /* 00000001 CULL_FACE_ENABLE */
+ dd_emit(ctx, 1, 1); /* 00000003 CULL_FACE */
+ dd_emit(ctx, 1, 0); /* 00000001 FRONT_FACE */
+ dd_emit(ctx, 1, 2); /* 00000003 POLYGON_MODE_FRONT */
+ dd_emit(ctx, 1, 0x1000); /* 00007fff UNK141C */
+ if (dev_priv->chipset != 0x50) {
+ dd_emit(ctx, 1, 0xe00); /* 7fff */
+ dd_emit(ctx, 1, 0x1000); /* 7fff */
+ dd_emit(ctx, 1, 0x1e00); /* 7fff */
+ }
+ dd_emit(ctx, 1, 0); /* 00000001 BEGIN_END_ACTIVE */
+ dd_emit(ctx, 1, 1); /* 00000001 POLYGON_MODE_??? */
+ dd_emit(ctx, 1, 1); /* 000000ff GP_REG_ALLOC_TEMP / 4 rounded up */
+ dd_emit(ctx, 1, 1); /* 000000ff FP_REG_ALLOC_TEMP... without /4? */
+ dd_emit(ctx, 1, 1); /* 000000ff VP_REG_ALLOC_TEMP / 4 rounded up */
+ dd_emit(ctx, 1, 1); /* 00000001 */
+ dd_emit(ctx, 1, 0); /* 00000001 */
+ dd_emit(ctx, 1, 0); /* 00000001 VTX_ATTR_MASK_UNK0 nonempty */
+ dd_emit(ctx, 1, 0); /* 00000001 VTX_ATTR_MASK_UNK1 nonempty */
+ dd_emit(ctx, 1, 0x200); /* 0003ffff GP_VERTEX_OUTPUT_COUNT*GP_REG_ALLOC_RESULT */
+ if (IS_NVA3F(dev_priv->chipset))
+ dd_emit(ctx, 1, 0x200);
+ dd_emit(ctx, 1, 0); /* 00000001 */
+ if (dev_priv->chipset < 0xa0) {
+ dd_emit(ctx, 1, 1); /* 00000001 */
+ dd_emit(ctx, 1, 0x70); /* 000000ff */
+ dd_emit(ctx, 1, 0x80); /* 000000ff */
+ dd_emit(ctx, 1, 0); /* 000000ff */
+ dd_emit(ctx, 1, 0); /* 00000001 */
+ dd_emit(ctx, 1, 1); /* 00000001 */
+ dd_emit(ctx, 1, 0x70); /* 000000ff */
+ dd_emit(ctx, 1, 0x80); /* 000000ff */
+ dd_emit(ctx, 1, 0); /* 000000ff */
+ } else {
+ dd_emit(ctx, 1, 1); /* 00000001 */
+ dd_emit(ctx, 1, 0xf0); /* 000000ff */
+ dd_emit(ctx, 1, 0xff); /* 000000ff */
+ dd_emit(ctx, 1, 0); /* 000000ff */
+ dd_emit(ctx, 1, 0); /* 00000001 */
+ dd_emit(ctx, 1, 1); /* 00000001 */
+ dd_emit(ctx, 1, 0xf0); /* 000000ff */
+ dd_emit(ctx, 1, 0xff); /* 000000ff */
+ dd_emit(ctx, 1, 0); /* 000000ff */
+ dd_emit(ctx, 1, 9); /* 0000003f UNK114C.COMP,SIZE */
+ }
+
+ /* eng2d state */
+ dd_emit(ctx, 1, 0); /* 00000001 eng2d COLOR_KEY_ENABLE */
+ dd_emit(ctx, 1, 0); /* 00000007 eng2d COLOR_KEY_FORMAT */
+ dd_emit(ctx, 1, 1); /* ffffffff eng2d DST_DEPTH */
+ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d DST_FORMAT */
+ dd_emit(ctx, 1, 0); /* ffffffff eng2d DST_LAYER */
+ dd_emit(ctx, 1, 1); /* 00000001 eng2d DST_LINEAR */
+ dd_emit(ctx, 1, 0); /* 00000007 eng2d PATTERN_COLOR_FORMAT */
+ dd_emit(ctx, 1, 0); /* 00000007 eng2d OPERATION */
+ dd_emit(ctx, 1, 0); /* 00000003 eng2d PATTERN_SELECT */
+ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d SIFC_FORMAT */
+ dd_emit(ctx, 1, 0); /* 00000001 eng2d SIFC_BITMAP_ENABLE */
+ dd_emit(ctx, 1, 2); /* 00000003 eng2d SIFC_BITMAP_UNK808 */
+ dd_emit(ctx, 1, 0); /* ffffffff eng2d BLIT_DU_DX_FRACT */
+ dd_emit(ctx, 1, 1); /* ffffffff eng2d BLIT_DU_DX_INT */
+ dd_emit(ctx, 1, 0); /* ffffffff eng2d BLIT_DV_DY_FRACT */
+ dd_emit(ctx, 1, 1); /* ffffffff eng2d BLIT_DV_DY_INT */
+ dd_emit(ctx, 1, 0); /* 00000001 eng2d BLIT_CONTROL_FILTER */
+ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d DRAW_COLOR_FORMAT */
+ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d SRC_FORMAT */
+ dd_emit(ctx, 1, 1); /* 00000001 eng2d SRC_LINEAR #2 */
+
+ num = ctx->ctxvals_pos - base;
+ ctx->ctxvals_pos = base;
+ if (IS_NVA3F(dev_priv->chipset))
+ cp_ctx(ctx, 0x404800, num);
+ else
+ cp_ctx(ctx, 0x405400, num);
+}
+
/*
* xfer areas. These are a pain.
*
@@ -990,28 +1130,33 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
* without the help of ctxprog.
*/
-static inline void
+static void
xf_emit(struct nouveau_grctx *ctx, int num, uint32_t val) {
int i;
if (val && ctx->mode == NOUVEAU_GRCTX_VALS)
for (i = 0; i < num; i++)
- nv_wo32(ctx->dev, ctx->data, ctx->ctxvals_pos + (i << 3), val);
+ nv_wo32(ctx->data, 4 * (ctx->ctxvals_pos + (i << 3)), val);
ctx->ctxvals_pos += num << 3;
}
/* Gene declarations... */
+static void nv50_graph_construct_gene_dispatch(struct nouveau_grctx *ctx);
static void nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx);
-static void nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_ccache(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk10xx(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk14xx(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_zcull(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_clipid(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk24xx(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_vfetch(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_eng2d(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_csched(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk1cxx(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_strmout(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk34xx(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_ropm1(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_ropm2(struct nouveau_grctx *ctx);
static void nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx);
static void nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx);
@@ -1030,102 +1175,32 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx)
if (dev_priv->chipset < 0xa0) {
/* Strand 0 */
ctx->ctxvals_pos = offset;
- switch (dev_priv->chipset) {
- case 0x50:
- xf_emit(ctx, 0x99, 0);
- break;
- case 0x84:
- case 0x86:
- xf_emit(ctx, 0x384, 0);
- break;
- case 0x92:
- case 0x94:
- case 0x96:
- case 0x98:
- xf_emit(ctx, 0x380, 0);
- break;
- }
- nv50_graph_construct_gene_m2mf (ctx);
- switch (dev_priv->chipset) {
- case 0x50:
- case 0x84:
- case 0x86:
- case 0x98:
- xf_emit(ctx, 0x4c4, 0);
- break;
- case 0x92:
- case 0x94:
- case 0x96:
- xf_emit(ctx, 0x984, 0);
- break;
- }
- nv50_graph_construct_gene_unk5(ctx);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 0xa, 0);
- else
- xf_emit(ctx, 0xb, 0);
- nv50_graph_construct_gene_unk4(ctx);
- nv50_graph_construct_gene_unk3(ctx);
+ nv50_graph_construct_gene_dispatch(ctx);
+ nv50_graph_construct_gene_m2mf(ctx);
+ nv50_graph_construct_gene_unk24xx(ctx);
+ nv50_graph_construct_gene_clipid(ctx);
+ nv50_graph_construct_gene_zcull(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
/* Strand 1 */
ctx->ctxvals_pos = offset + 0x1;
- nv50_graph_construct_gene_unk6(ctx);
- nv50_graph_construct_gene_unk7(ctx);
- nv50_graph_construct_gene_unk8(ctx);
- switch (dev_priv->chipset) {
- case 0x50:
- case 0x92:
- xf_emit(ctx, 0xfb, 0);
- break;
- case 0x84:
- xf_emit(ctx, 0xd3, 0);
- break;
- case 0x94:
- case 0x96:
- xf_emit(ctx, 0xab, 0);
- break;
- case 0x86:
- case 0x98:
- xf_emit(ctx, 0x6b, 0);
- break;
- }
- xf_emit(ctx, 2, 0x4e3bfdf);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 0xb, 0);
- xf_emit(ctx, 2, 0x4e3bfdf);
+ nv50_graph_construct_gene_vfetch(ctx);
+ nv50_graph_construct_gene_eng2d(ctx);
+ nv50_graph_construct_gene_csched(ctx);
+ nv50_graph_construct_gene_ropm1(ctx);
+ nv50_graph_construct_gene_ropm2(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
/* Strand 2 */
ctx->ctxvals_pos = offset + 0x2;
- switch (dev_priv->chipset) {
- case 0x50:
- case 0x92:
- xf_emit(ctx, 0xa80, 0);
- break;
- case 0x84:
- xf_emit(ctx, 0xa7e, 0);
- break;
- case 0x94:
- case 0x96:
- xf_emit(ctx, 0xa7c, 0);
- break;
- case 0x86:
- case 0x98:
- xf_emit(ctx, 0xa7a, 0);
- break;
- }
- xf_emit(ctx, 1, 0x3fffff);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x1fff);
- xf_emit(ctx, 0xe, 0);
- nv50_graph_construct_gene_unk9(ctx);
- nv50_graph_construct_gene_unk2(ctx);
- nv50_graph_construct_gene_unk1(ctx);
- nv50_graph_construct_gene_unk10(ctx);
+ nv50_graph_construct_gene_ccache(ctx);
+ nv50_graph_construct_gene_unk1cxx(ctx);
+ nv50_graph_construct_gene_strmout(ctx);
+ nv50_graph_construct_gene_unk14xx(ctx);
+ nv50_graph_construct_gene_unk10xx(ctx);
+ nv50_graph_construct_gene_unk34xx(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
@@ -1150,86 +1225,46 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx)
} else {
/* Strand 0 */
ctx->ctxvals_pos = offset;
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0x385, 0);
- else
- xf_emit(ctx, 0x384, 0);
+ nv50_graph_construct_gene_dispatch(ctx);
nv50_graph_construct_gene_m2mf(ctx);
- xf_emit(ctx, 0x950, 0);
- nv50_graph_construct_gene_unk10(ctx);
- xf_emit(ctx, 1, 0x0fac6881);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 3, 0);
- }
- nv50_graph_construct_gene_unk8(ctx);
- if (dev_priv->chipset == 0xa0)
- xf_emit(ctx, 0x189, 0);
- else if (dev_priv->chipset == 0xa3)
- xf_emit(ctx, 0xd5, 0);
- else if (dev_priv->chipset == 0xa5)
- xf_emit(ctx, 0x99, 0);
- else if (dev_priv->chipset == 0xaa)
- xf_emit(ctx, 0x65, 0);
- else
- xf_emit(ctx, 0x6d, 0);
- nv50_graph_construct_gene_unk9(ctx);
+ nv50_graph_construct_gene_unk34xx(ctx);
+ nv50_graph_construct_gene_csched(ctx);
+ nv50_graph_construct_gene_unk1cxx(ctx);
+ nv50_graph_construct_gene_strmout(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
/* Strand 1 */
ctx->ctxvals_pos = offset + 1;
- nv50_graph_construct_gene_unk1(ctx);
+ nv50_graph_construct_gene_unk10xx(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
/* Strand 2 */
ctx->ctxvals_pos = offset + 2;
- if (dev_priv->chipset == 0xa0) {
- nv50_graph_construct_gene_unk2(ctx);
- }
- xf_emit(ctx, 0x36, 0);
- nv50_graph_construct_gene_unk5(ctx);
+ if (dev_priv->chipset == 0xa0)
+ nv50_graph_construct_gene_unk14xx(ctx);
+ nv50_graph_construct_gene_unk24xx(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
/* Strand 3 */
ctx->ctxvals_pos = offset + 3;
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- nv50_graph_construct_gene_unk6(ctx);
+ nv50_graph_construct_gene_vfetch(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
/* Strand 4 */
ctx->ctxvals_pos = offset + 4;
- if (dev_priv->chipset == 0xa0)
- xf_emit(ctx, 0xa80, 0);
- else if (dev_priv->chipset == 0xa3)
- xf_emit(ctx, 0xa7c, 0);
- else
- xf_emit(ctx, 0xa7a, 0);
- xf_emit(ctx, 1, 0x3fffff);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x1fff);
+ nv50_graph_construct_gene_ccache(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
/* Strand 5 */
ctx->ctxvals_pos = offset + 5;
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 0xb, 0);
- xf_emit(ctx, 2, 0x4e3bfdf);
- xf_emit(ctx, 3, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 2, 0x4e3bfdf);
- xf_emit(ctx, 2, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 0);
+ nv50_graph_construct_gene_ropm2(ctx);
+ nv50_graph_construct_gene_ropm1(ctx);
+ /* per-ROP context */
for (i = 0; i < 8; i++)
if (units & (1<<(i+16)))
nv50_graph_construct_gene_ropc(ctx);
@@ -1238,10 +1273,9 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx)
/* Strand 6 */
ctx->ctxvals_pos = offset + 6;
- nv50_graph_construct_gene_unk3(ctx);
- xf_emit(ctx, 0xb, 0);
- nv50_graph_construct_gene_unk4(ctx);
- nv50_graph_construct_gene_unk7(ctx);
+ nv50_graph_construct_gene_zcull(ctx);
+ nv50_graph_construct_gene_clipid(ctx);
+ nv50_graph_construct_gene_eng2d(ctx);
if (units & (1 << 0))
nv50_graph_construct_xfer_tp(ctx);
if (units & (1 << 1))
@@ -1269,7 +1303,7 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx)
if (units & (1 << 9))
nv50_graph_construct_xfer_tp(ctx);
} else {
- nv50_graph_construct_gene_unk2(ctx);
+ nv50_graph_construct_gene_unk14xx(ctx);
}
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
@@ -1290,9 +1324,70 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx)
*/
static void
+nv50_graph_construct_gene_dispatch(struct nouveau_grctx *ctx)
+{
+ /* start of strand 0 */
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ /* SEEK */
+ if (dev_priv->chipset == 0x50)
+ xf_emit(ctx, 5, 0);
+ else if (!IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 6, 0);
+ else
+ xf_emit(ctx, 4, 0);
+ /* SEEK */
+ /* the PGRAPH's internal FIFO */
+ if (dev_priv->chipset == 0x50)
+ xf_emit(ctx, 8*3, 0);
+ else
+ xf_emit(ctx, 0x100*3, 0);
+ /* and another bonus slot?!? */
+ xf_emit(ctx, 3, 0);
+ /* and YET ANOTHER bonus slot? */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 3, 0);
+ /* SEEK */
+ /* CTX_SWITCH: caches of gr objects bound to subchannels. 8 values, last used index */
+ xf_emit(ctx, 9, 0);
+ /* SEEK */
+ xf_emit(ctx, 9, 0);
+ /* SEEK */
+ xf_emit(ctx, 9, 0);
+ /* SEEK */
+ xf_emit(ctx, 9, 0);
+ /* SEEK */
+ if (dev_priv->chipset < 0x90)
+ xf_emit(ctx, 4, 0);
+ /* SEEK */
+ xf_emit(ctx, 2, 0);
+ /* SEEK */
+ xf_emit(ctx, 6*2, 0);
+ xf_emit(ctx, 2, 0);
+ /* SEEK */
+ xf_emit(ctx, 2, 0);
+ /* SEEK */
+ xf_emit(ctx, 6*2, 0);
+ xf_emit(ctx, 2, 0);
+ /* SEEK */
+ if (dev_priv->chipset == 0x50)
+ xf_emit(ctx, 0x1c, 0);
+ else if (dev_priv->chipset < 0xa0)
+ xf_emit(ctx, 0x1e, 0);
+ else
+ xf_emit(ctx, 0x22, 0);
+ /* SEEK */
+ xf_emit(ctx, 0x15, 0);
+}
+
+static void
nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx)
{
- /* m2mf state */
+ /* Strand 0, right after dispatch */
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ int smallm2mf = 0;
+ if (dev_priv->chipset < 0x92 || dev_priv->chipset == 0x98)
+ smallm2mf = 1;
+ /* SEEK */
xf_emit (ctx, 1, 0); /* DMA_NOTIFY instance >> 4 */
xf_emit (ctx, 1, 0); /* DMA_BUFFER_IN instance >> 4 */
xf_emit (ctx, 1, 0); /* DMA_BUFFER_OUT instance >> 4 */
@@ -1319,427 +1414,975 @@ nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx)
xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT */
xf_emit (ctx, 1, 0); /* OFFSET_IN_HIGH */
xf_emit (ctx, 1, 0); /* OFFSET_OUT_HIGH */
+ /* SEEK */
+ if (smallm2mf)
+ xf_emit(ctx, 0x40, 0); /* 20 * ffffffff, 3ffff */
+ else
+ xf_emit(ctx, 0x100, 0); /* 80 * ffffffff, 3ffff */
+ xf_emit(ctx, 4, 0); /* 1f/7f, 0, 1f/7f, 0 [1f for smallm2mf, 7f otherwise] */
+ /* SEEK */
+ if (smallm2mf)
+ xf_emit(ctx, 0x400, 0); /* ffffffff */
+ else
+ xf_emit(ctx, 0x800, 0); /* ffffffff */
+ xf_emit(ctx, 4, 0); /* ff/1ff, 0, 0, 0 [ff for smallm2mf, 1ff otherwise] */
+ /* SEEK */
+ xf_emit(ctx, 0x40, 0); /* 20 * bits ffffffff, 3ffff */
+ xf_emit(ctx, 0x6, 0); /* 1f, 0, 1f, 0, 1f, 0 */
}
static void
-nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_ccache(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- /* end of area 2 on pre-NVA0, area 1 on NVAx */
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x80);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0x80c14);
- xf_emit(ctx, 1, 0);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 1, 0x3ff);
- else
- xf_emit(ctx, 1, 0x7ff);
+ xf_emit(ctx, 2, 0); /* RO */
+ xf_emit(ctx, 0x800, 0); /* ffffffff */
switch (dev_priv->chipset) {
case 0x50:
- case 0x86:
- case 0x98:
- case 0xaa:
- case 0xac:
- xf_emit(ctx, 0x542, 0);
+ case 0x92:
+ case 0xa0:
+ xf_emit(ctx, 0x2b, 0);
break;
case 0x84:
- case 0x92:
+ xf_emit(ctx, 0x29, 0);
+ break;
case 0x94:
case 0x96:
- xf_emit(ctx, 0x942, 0);
- break;
- case 0xa0:
case 0xa3:
- xf_emit(ctx, 0x2042, 0);
+ xf_emit(ctx, 0x27, 0);
break;
+ case 0x86:
+ case 0x98:
case 0xa5:
case 0xa8:
- xf_emit(ctx, 0x842, 0);
+ case 0xaa:
+ case 0xac:
+ case 0xaf:
+ xf_emit(ctx, 0x25, 0);
break;
}
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x80);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x27);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x26);
- xf_emit(ctx, 3, 0);
+ /* CB bindings, 0x80 of them. first word is address >> 8, second is
+ * size >> 4 | valid << 24 */
+ xf_emit(ctx, 0x100, 0); /* ffffffff CB_DEF */
+ xf_emit(ctx, 1, 0); /* 0000007f CB_ADDR_BUFFER */
+ xf_emit(ctx, 1, 0); /* 0 */
+ xf_emit(ctx, 0x30, 0); /* ff SET_PROGRAM_CB */
+ xf_emit(ctx, 1, 0); /* 3f last SET_PROGRAM_CB */
+ xf_emit(ctx, 4, 0); /* RO */
+ xf_emit(ctx, 0x100, 0); /* ffffffff */
+ xf_emit(ctx, 8, 0); /* 1f, 0, 0, ... */
+ xf_emit(ctx, 8, 0); /* ffffffff */
+ xf_emit(ctx, 4, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 3 */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_CODE_CB */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_TIC */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_TSC */
+ xf_emit(ctx, 1, 0); /* 00000001 LINKED_TSC */
+ xf_emit(ctx, 1, 0); /* 000000ff TIC_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff TIC_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */
+ xf_emit(ctx, 1, 0); /* 000000ff TSC_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff TSC_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */
+ xf_emit(ctx, 1, 0); /* 000000ff VP_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff VP_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0); /* 00ffffff VP_START_ID */
+ xf_emit(ctx, 1, 0); /* 000000ff CB_DEF_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff CB_DEF_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0); /* 000000ff GP_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff GP_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0); /* 00ffffff GP_START_ID */
+ xf_emit(ctx, 1, 0); /* 000000ff FP_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff FP_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0); /* 00ffffff FP_START_ID */
}
static void
-nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_unk10xx(struct nouveau_grctx *ctx)
{
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ int i;
/* end of area 2 on pre-NVA0, area 1 on NVAx */
- xf_emit(ctx, 0x10, 0x04000000);
- xf_emit(ctx, 0x24, 0);
- xf_emit(ctx, 2, 0x04e3bfdf);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x1fe21);
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */
+ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */
+ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */
+ if (dev_priv->chipset == 0x50)
+ xf_emit(ctx, 1, 0x3ff);
+ else
+ xf_emit(ctx, 1, 0x7ff); /* 000007ff */
+ xf_emit(ctx, 1, 0); /* 111/113 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ for (i = 0; i < 8; i++) {
+ switch (dev_priv->chipset) {
+ case 0x50:
+ case 0x86:
+ case 0x98:
+ case 0xaa:
+ case 0xac:
+ xf_emit(ctx, 0xa0, 0); /* ffffffff */
+ break;
+ case 0x84:
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ xf_emit(ctx, 0x120, 0);
+ break;
+ case 0xa5:
+ case 0xa8:
+ xf_emit(ctx, 0x100, 0); /* ffffffff */
+ break;
+ case 0xa0:
+ case 0xa3:
+ case 0xaf:
+ xf_emit(ctx, 0x400, 0); /* ffffffff */
+ break;
+ }
+ xf_emit(ctx, 4, 0); /* 3f, 0, 0, 0 */
+ xf_emit(ctx, 4, 0); /* ffffffff */
+ }
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_TEMP */
+ xf_emit(ctx, 1, 1); /* 00000001 RASTERIZE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 0x27); /* 000000ff UNK0FD4 */
+ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */
+ xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+}
+
+static void
+nv50_graph_construct_gene_unk34xx(struct nouveau_grctx *ctx)
+{
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ /* end of area 2 on pre-NVA0, area 1 on NVAx */
+ xf_emit(ctx, 1, 0); /* 00000001 VIEWPORT_CLIP_RECTS_EN */
+ xf_emit(ctx, 1, 0); /* 00000003 VIEWPORT_CLIP_MODE */
+ xf_emit(ctx, 0x10, 0x04000000); /* 07ffffff VIEWPORT_CLIP_HORIZ*8, VIEWPORT_CLIP_VERT*8 */
+ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_STIPPLE_ENABLE */
+ xf_emit(ctx, 0x20, 0); /* ffffffff POLYGON_STIPPLE */
+ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0D64 */
+ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0DF4 */
+ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0x1fe21); /* 0001ffff tesla UNK0FAC */
+ if (dev_priv->chipset >= 0xa0)
+ xf_emit(ctx, 1, 0x0fac6881);
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 1);
+ xf_emit(ctx, 3, 0);
+ }
}
static void
-nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_unk14xx(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
/* middle of area 2 on pre-NVA0, beginning of area 2 on NVA0, area 7 on >NVA0 */
if (dev_priv->chipset != 0x50) {
- xf_emit(ctx, 5, 0);
- xf_emit(ctx, 1, 0x80c14);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x804);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0x8100c12);
+ xf_emit(ctx, 5, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ xf_emit(ctx, 1, 0x804); /* 00000fff SEMANTIC_CLIP */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 2, 4); /* 7f, ff */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
}
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x10);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 3, 0);
- else
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0x804);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x1a);
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 1, 0); /* 000000ff VP_CLIP_DISTANCE_ENABLE */
if (dev_priv->chipset != 0x50)
- xf_emit(ctx, 1, 0x7f);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x80c14);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x8100c12);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x10);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x8100c12);
- xf_emit(ctx, 6, 0);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 1, 0x3ff);
- else
- xf_emit(ctx, 1, 0x7ff);
- xf_emit(ctx, 1, 0x80c14);
- xf_emit(ctx, 0x38, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x10);
- xf_emit(ctx, 0x38, 0);
- xf_emit(ctx, 2, 0x88);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 0x16, 0);
- xf_emit(ctx, 1, 0x26);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x3f800000);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 4, 0);
- else
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x1a);
- xf_emit(ctx, 1, 0x10);
+ xf_emit(ctx, 1, 0); /* 3ff */
+ xf_emit(ctx, 1, 0); /* 000000ff tesla UNK1940 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0D7C */
+ xf_emit(ctx, 1, 0x804); /* 00000fff SEMANTIC_CLIP */
+ xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */
+ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */
if (dev_priv->chipset != 0x50)
- xf_emit(ctx, 0x28, 0);
+ xf_emit(ctx, 1, 0x7f); /* 000000ff tesla UNK0FFC */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 1); /* 00000001 SHADE_MODEL */
+ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0D7C */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0F8C */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 4, 0); /* ffffffff NOPERSPECTIVE_BITMAP */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 0); /* 0000000f */
+ if (dev_priv->chipset == 0x50)
+ xf_emit(ctx, 1, 0x3ff); /* 000003ff tesla UNK0D68 */
else
- xf_emit(ctx, 0x25, 0);
- xf_emit(ctx, 1, 0x52);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x26);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x1a);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x00ffff00);
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 1, 0x7ff); /* 000007ff tesla UNK0D68 */
+ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */
+ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */
+ xf_emit(ctx, 0x30, 0); /* ffffffff VIEWPORT_SCALE: X0, Y0, Z0, X1, Y1, ... */
+ xf_emit(ctx, 3, 0); /* f, 0, 0 */
+ xf_emit(ctx, 3, 0); /* ffffffff last VIEWPORT_SCALE? */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */
+ xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 0x30, 0); /* ffffffff VIEWPORT_TRANSLATE */
+ xf_emit(ctx, 3, 0); /* f, 0, 0 */
+ xf_emit(ctx, 3, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 2, 0x88); /* 000001ff tesla UNK19D8 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */
+ xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */
+ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */
+ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */
+ xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */
+ xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 0); /* 0000000f */
+ xf_emit(ctx, 1, 0x3f800000); /* ffffffff LINE_WIDTH */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */
+ xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */
+ if (dev_priv->chipset != 0x50) {
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ }
+ xf_emit(ctx, 0x20, 0); /* 10xbits ffffffff, 3fffff. SCISSOR_* */
+ xf_emit(ctx, 1, 0); /* f */
+ xf_emit(ctx, 1, 0); /* 0? */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 003fffff */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 0x52); /* 000001ff SEMANTIC_PTSZ */
+ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */
+ xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */
+ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */
+ xf_emit(ctx, 1, 0); /* 0000000f */
}
static void
-nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_zcull(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- /* end of area 0 on pre-NVA0, beginning of area 6 on NVAx */
- xf_emit(ctx, 1, 0x3f);
- xf_emit(ctx, 0xa, 0);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 2, 0x04000000);
- xf_emit(ctx, 8, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 4);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 0x10, 0);
- else
- xf_emit(ctx, 0x11, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x1001);
- xf_emit(ctx, 4, 0xffff);
- xf_emit(ctx, 0x20, 0);
- xf_emit(ctx, 0x10, 0x3f800000);
- xf_emit(ctx, 1, 0x10);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 1, 0);
- else
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 3);
- xf_emit(ctx, 2, 0);
+ /* end of strand 0 on pre-NVA0, beginning of strand 6 on NVAx */
+ /* SEEK */
+ xf_emit(ctx, 1, 0x3f); /* 0000003f UNK1590 */
+ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */
+ xf_emit(ctx, 2, 0x04000000); /* 07ffffff tesla UNK0D6C */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 0); /* 00000001 CLIPID_ENABLE */
+ xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */
+ xf_emit(ctx, 1, 0); /* 0000ffff */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK0FB0 */
+ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_STIPPLE_ENABLE */
+ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */
+ xf_emit(ctx, 1, 0); /* 000000ff CLEAR_STENCIL */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ if (dev_priv->chipset != 0x50)
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1108 */
+ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0x1001); /* 00001fff ZETA_ARRAY_MODE */
+ /* SEEK */
+ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */
+ xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */
+ xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */
+ xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */
+ xf_emit(ctx, 1, 0x10); /* 7f/ff/3ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 1, 0); /* 00000001 VIEWPORT_CLIP_RECTS_EN */
+ xf_emit(ctx, 1, 3); /* 00000003 FP_CTRL_UNK196C */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1968 */
+ if (dev_priv->chipset != 0x50)
+ xf_emit(ctx, 1, 0); /* 0fffffff tesla UNK1104 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK151C */
}
static void
-nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_clipid(struct nouveau_grctx *ctx)
{
- /* middle of area 0 on pre-NVA0, middle of area 6 on NVAx */
- xf_emit(ctx, 2, 0x04000000);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x80);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x80);
- xf_emit(ctx, 1, 0);
+ /* middle of strand 0 on pre-NVA0 [after 24xx], middle of area 6 on NVAx */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 00000007 UNK0FB4 */
+ /* SEEK */
+ xf_emit(ctx, 4, 0); /* 07ffffff CLIPID_REGION_HORIZ */
+ xf_emit(ctx, 4, 0); /* 07ffffff CLIPID_REGION_VERT */
+ xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */
+ xf_emit(ctx, 2, 0x04000000); /* 07ffffff UNK1508 */
+ xf_emit(ctx, 1, 0); /* 00000001 CLIPID_ENABLE */
+ xf_emit(ctx, 1, 0x80); /* 00003fff CLIPID_WIDTH */
+ xf_emit(ctx, 1, 0); /* 000000ff CLIPID_ID */
+ xf_emit(ctx, 1, 0); /* 000000ff CLIPID_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff CLIPID_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0x80); /* 00003fff CLIPID_HEIGHT */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_CLIPID */
}
static void
-nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_unk24xx(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- /* middle of area 0 on pre-NVA0 [after m2mf], end of area 2 on NVAx */
- xf_emit(ctx, 2, 4);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0x1c4d, 0);
+ int i;
+ /* middle of strand 0 on pre-NVA0 [after m2mf], end of strand 2 on NVAx */
+ /* SEEK */
+ xf_emit(ctx, 0x33, 0);
+ /* SEEK */
+ xf_emit(ctx, 2, 0);
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 4, 0); /* RO */
+ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */
+ xf_emit(ctx, 1, 0); /* 1ff */
+ xf_emit(ctx, 8, 0); /* 0? */
+ xf_emit(ctx, 9, 0); /* ffffffff, 7ff */
+
+ xf_emit(ctx, 4, 0); /* RO */
+ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */
+ xf_emit(ctx, 1, 0); /* 1ff */
+ xf_emit(ctx, 8, 0); /* 0? */
+ xf_emit(ctx, 9, 0); /* ffffffff, 7ff */
+ }
else
- xf_emit(ctx, 0x1c4b, 0);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0x8100c12);
+ {
+ xf_emit(ctx, 0xc, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */
+ xf_emit(ctx, 1, 0); /* 1ff */
+ xf_emit(ctx, 8, 0); /* 0? */
+
+ /* SEEK */
+ xf_emit(ctx, 0xc, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */
+ xf_emit(ctx, 1, 0); /* 1ff */
+ xf_emit(ctx, 8, 0); /* 0? */
+ }
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
if (dev_priv->chipset != 0x50)
- xf_emit(ctx, 1, 3);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x8100c12);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x80c14);
- xf_emit(ctx, 1, 1);
+ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */
+ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */
+ xf_emit(ctx, 1, 1); /* 00000001 */
+ /* SEEK */
if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0x80c14);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x8100c12);
- xf_emit(ctx, 1, 0x27);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0x3c1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0x16, 0);
- xf_emit(ctx, 1, 0x8100c12);
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 2, 4); /* 000000ff */
+ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */
+ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 1, 0x27); /* 000000ff SEMANTIC_PRIM_ID */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000000f */
+ xf_emit(ctx, 1, 1); /* 00000001 */
+ for (i = 0; i < 10; i++) {
+ /* SEEK */
+ xf_emit(ctx, 0x40, 0); /* ffffffff */
+ xf_emit(ctx, 0x10, 0); /* 3, 0, 0.... */
+ xf_emit(ctx, 0x10, 0); /* ffffffff */
+ }
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_CTRL */
+ xf_emit(ctx, 1, 1); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 4, 0); /* ffffffff NOPERSPECTIVE_BITMAP */
+ xf_emit(ctx, 0x10, 0); /* 00ffffff POINT_COORD_REPLACE_MAP */
+ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ if (dev_priv->chipset != 0x50)
+ xf_emit(ctx, 1, 0); /* 000003ff */
}
static void
-nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_vfetch(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- /* beginning of area 1 on pre-NVA0 [after m2mf], area 3 on NVAx */
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0xf);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 8, 0);
- else
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0x20);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0x11, 0);
+ int acnt = 0x10, rep, i;
+ /* beginning of strand 1 on pre-NVA0, strand 3 on NVAx */
+ if (IS_NVA3F(dev_priv->chipset))
+ acnt = 0x20;
+ /* SEEK */
+ if (dev_priv->chipset >= 0xa0) {
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK13A4 */
+ xf_emit(ctx, 1, 1); /* 00000fff tesla UNK1318 */
+ }
+ xf_emit(ctx, 1, 0); /* ffffffff VERTEX_BUFFER_FIRST */
+ xf_emit(ctx, 1, 0); /* 00000001 PRIMITIVE_RESTART_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK0DE8 */
+ xf_emit(ctx, 1, 0); /* ffffffff PRIMITIVE_RESTART_INDEX */
+ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, acnt/8, 0); /* ffffffff VTX_ATR_MASK_UNK0DD0 */
+ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */
+ xf_emit(ctx, 1, 0x20); /* 0000ffff tesla UNK129C */
+ xf_emit(ctx, 1, 0); /* 000000ff turing UNK370??? */
+ xf_emit(ctx, 1, 0); /* 0000ffff turing USER_PARAM_COUNT */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0xb, 0); /* RO */
else if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 0xf, 0);
+ xf_emit(ctx, 0x9, 0); /* RO */
else
- xf_emit(ctx, 0xe, 0);
- xf_emit(ctx, 1, 0x1a);
- xf_emit(ctx, 0xd, 0);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 8);
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 0x8, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 00000001 EDGE_FLAG */
+ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */
+ /* SEEK */
+ xf_emit(ctx, 0xc, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 7f/ff */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */
+ xf_emit(ctx, 1, 4); /* 000001ff UNK1A28 */
+ xf_emit(ctx, 1, 8); /* 000001ff UNK0DF0 */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 1, 0x3ff);
+ xf_emit(ctx, 1, 0x3ff); /* 3ff tesla UNK0D68 */
else
- xf_emit(ctx, 1, 0x7ff);
+ xf_emit(ctx, 1, 0x7ff); /* 7ff tesla UNK0D68 */
if (dev_priv->chipset == 0xa8)
- xf_emit(ctx, 1, 0x1e00);
- xf_emit(ctx, 0xc, 0);
- xf_emit(ctx, 1, 0xf);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 0x125, 0);
- else if (dev_priv->chipset < 0xa0)
- xf_emit(ctx, 0x126, 0);
- else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa)
- xf_emit(ctx, 0x124, 0);
+ xf_emit(ctx, 1, 0x1e00); /* 7fff */
+ /* SEEK */
+ xf_emit(ctx, 0xc, 0); /* RO or close */
+ /* SEEK */
+ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */
+ if (dev_priv->chipset > 0x50 && dev_priv->chipset < 0xa0)
+ xf_emit(ctx, 2, 0); /* ffffffff */
else
- xf_emit(ctx, 0x1f7, 0);
- xf_emit(ctx, 1, 0xf);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 3, 0);
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0FD8 */
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 0x10, 0); /* 0? */
+ xf_emit(ctx, 2, 0); /* weird... */
+ xf_emit(ctx, 2, 0); /* RO */
+ } else {
+ xf_emit(ctx, 8, 0); /* 0? */
+ xf_emit(ctx, 1, 0); /* weird... */
+ xf_emit(ctx, 2, 0); /* RO */
+ }
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* ffffffff VB_ELEMENT_BASE */
+ xf_emit(ctx, 1, 0); /* ffffffff UNK1438 */
+ xf_emit(ctx, acnt, 0); /* 1 tesla UNK1000 */
+ if (dev_priv->chipset >= 0xa0)
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1118? */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_UNK90C */
+ xf_emit(ctx, 1, 0); /* f/1f */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_UNK90C */
+ xf_emit(ctx, 1, 0); /* f/1f */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* RO */
+ xf_emit(ctx, 2, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK111C? */
+ xf_emit(ctx, 1, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 000000ff UNK15F4_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff UNK15F4_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0); /* 000000ff UNK0F84_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff UNK0F84_ADDRESS_LOW */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* 00003fff VERTEX_ARRAY_ATTRIB_OFFSET */
+ xf_emit(ctx, 3, 0); /* f/1f */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* 00000fff VERTEX_ARRAY_STRIDE */
+ xf_emit(ctx, 3, 0); /* f/1f */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_LOW */
+ xf_emit(ctx, 3, 0); /* f/1f */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* 000000ff VERTEX_ARRAY_HIGH */
+ xf_emit(ctx, 3, 0); /* f/1f */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_LIMIT_LOW */
+ xf_emit(ctx, 3, 0); /* f/1f */
+ /* SEEK */
+ xf_emit(ctx, acnt, 0); /* 000000ff VERTEX_LIMIT_HIGH */
+ xf_emit(ctx, 3, 0); /* f/1f */
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, acnt, 0); /* f */
+ xf_emit(ctx, 3, 0); /* f/1f */
+ }
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 2, 0); /* RO */
+ else
+ xf_emit(ctx, 5, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* ffff DMA_VTXBUF */
+ /* SEEK */
+ if (dev_priv->chipset < 0xa0) {
+ xf_emit(ctx, 0x41, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 0x11, 0); /* RO */
+ } else if (!IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0x50, 0); /* RO */
else
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0xa1, 0);
+ xf_emit(ctx, 0x58, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, 1, 1); /* 1 UNK0DEC */
+ /* SEEK */
+ xf_emit(ctx, acnt*4, 0); /* ffffffff VTX_ATTR */
+ xf_emit(ctx, 4, 0); /* f/1f, 0, 0, 0 */
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0x1d, 0); /* RO */
else
- xf_emit(ctx, 0x5a, 0);
- xf_emit(ctx, 1, 0xf);
+ xf_emit(ctx, 0x16, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */
+ /* SEEK */
if (dev_priv->chipset < 0xa0)
- xf_emit(ctx, 0x834, 0);
- else if (dev_priv->chipset == 0xa0)
- xf_emit(ctx, 0x1873, 0);
- else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0x8ba, 0);
+ xf_emit(ctx, 8, 0); /* RO */
+ else if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0xc, 0); /* RO */
+ else
+ xf_emit(ctx, 7, 0); /* RO */
+ /* SEEK */
+ xf_emit(ctx, 0xa, 0); /* RO */
+ if (dev_priv->chipset == 0xa0)
+ rep = 0xc;
+ else
+ rep = 4;
+ for (i = 0; i < rep; i++) {
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0x20, 0); /* ffffffff */
+ xf_emit(ctx, 0x200, 0); /* ffffffff */
+ xf_emit(ctx, 4, 0); /* 7f/ff, 0, 0, 0 */
+ xf_emit(ctx, 4, 0); /* ffffffff */
+ }
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 113/111 */
+ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */
+ xf_emit(ctx, acnt/8, 0); /* ffffffff VTX_ATTR_MASK_UNK0DD0 */
+ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ /* SEEK */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 7, 0); /* weird... */
else
- xf_emit(ctx, 0x833, 0);
- xf_emit(ctx, 1, 0xf);
- xf_emit(ctx, 0xf, 0);
+ xf_emit(ctx, 5, 0); /* weird... */
}
static void
-nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_eng2d(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 6 on NVAx */
- xf_emit(ctx, 2, 0);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 2, 1);
- else
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0x100);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 8);
- xf_emit(ctx, 5, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 3, 1);
- xf_emit(ctx, 1, 0xcf);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 6, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 3, 1);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x15);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x4444480);
- xf_emit(ctx, 0x37, 0);
+ /* middle of strand 1 on pre-NVA0 [after vfetch], middle of strand 6 on NVAx */
+ /* SEEK */
+ xf_emit(ctx, 2, 0); /* 0001ffff CLIP_X, CLIP_Y */
+ xf_emit(ctx, 2, 0); /* 0000ffff CLIP_W, CLIP_H */
+ xf_emit(ctx, 1, 0); /* 00000001 CLIP_ENABLE */
+ if (dev_priv->chipset < 0xa0) {
+ /* this is useless on everything but the original NV50,
+ * guess they forgot to nuke it. Or just didn't bother. */
+ xf_emit(ctx, 2, 0); /* 0000ffff IFC_CLIP_X, Y */
+ xf_emit(ctx, 2, 1); /* 0000ffff IFC_CLIP_W, H */
+ xf_emit(ctx, 1, 0); /* 00000001 IFC_CLIP_ENABLE */
+ }
+ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */
+ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_WIDTH */
+ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_HEIGHT */
+ xf_emit(ctx, 1, 0x11); /* 3f[NV50]/7f[NV84+] DST_FORMAT */
+ xf_emit(ctx, 1, 0); /* 0001ffff DRAW_POINT_X */
+ xf_emit(ctx, 1, 8); /* 0000000f DRAW_UNK58C */
+ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DST_X_FRACT */
+ xf_emit(ctx, 1, 0); /* 0001ffff SIFC_DST_X_INT */
+ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DST_Y_FRACT */
+ xf_emit(ctx, 1, 0); /* 0001ffff SIFC_DST_Y_INT */
+ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DX_DU_FRACT */
+ xf_emit(ctx, 1, 1); /* 0001ffff SIFC_DX_DU_INT */
+ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DY_DV_FRACT */
+ xf_emit(ctx, 1, 1); /* 0001ffff SIFC_DY_DV_INT */
+ xf_emit(ctx, 1, 1); /* 0000ffff SIFC_WIDTH */
+ xf_emit(ctx, 1, 1); /* 0000ffff SIFC_HEIGHT */
+ xf_emit(ctx, 1, 0xcf); /* 000000ff SIFC_FORMAT */
+ xf_emit(ctx, 1, 2); /* 00000003 SIFC_BITMAP_UNK808 */
+ xf_emit(ctx, 1, 0); /* 00000003 SIFC_BITMAP_LINE_PACK_MODE */
+ xf_emit(ctx, 1, 0); /* 00000001 SIFC_BITMAP_LSB_FIRST */
+ xf_emit(ctx, 1, 0); /* 00000001 SIFC_BITMAP_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000ffff BLIT_DST_X */
+ xf_emit(ctx, 1, 0); /* 0000ffff BLIT_DST_Y */
+ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DU_DX_FRACT */
+ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DU_DX_INT */
+ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DV_DY_FRACT */
+ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DV_DY_INT */
+ xf_emit(ctx, 1, 1); /* 0000ffff BLIT_DST_W */
+ xf_emit(ctx, 1, 1); /* 0000ffff BLIT_DST_H */
+ xf_emit(ctx, 1, 0); /* 000fffff BLIT_SRC_X_FRACT */
+ xf_emit(ctx, 1, 0); /* 0001ffff BLIT_SRC_X_INT */
+ xf_emit(ctx, 1, 0); /* 000fffff BLIT_SRC_Y_FRACT */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK888 */
+ xf_emit(ctx, 1, 4); /* 0000003f UNK884 */
+ xf_emit(ctx, 1, 0); /* 00000007 UNK880 */
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK0FB8 */
+ xf_emit(ctx, 1, 0x15); /* 000000ff tesla UNK128C */
+ xf_emit(ctx, 2, 0); /* 00000007, ffff0ff3 */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK260 */
+ xf_emit(ctx, 1, 0x4444480); /* 1fffffff UNK870 */
+ /* SEEK */
+ xf_emit(ctx, 0x10, 0);
+ /* SEEK */
+ xf_emit(ctx, 0x27, 0);
}
static void
-nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_csched(struct nouveau_grctx *ctx)
{
- /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 0 on NVAx */
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0x8100c12);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0x100);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x10001);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x10001);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x10001);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 2);
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ /* middle of strand 1 on pre-NVA0 [after eng2d], middle of strand 0 on NVAx */
+ /* SEEK */
+ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY... what is it doing here??? */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */
+ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* ffffffff turing UNK364 */
+ xf_emit(ctx, 1, 0); /* 0000000f turing UNK36C */
+ xf_emit(ctx, 1, 0); /* 0000ffff USER_PARAM_COUNT */
+ xf_emit(ctx, 1, 0x100); /* 00ffffff turing UNK384 */
+ xf_emit(ctx, 1, 0); /* 0000000f turing UNK2A0 */
+ xf_emit(ctx, 1, 0); /* 0000ffff GRIDID */
+ xf_emit(ctx, 1, 0x10001); /* ffffffff GRIDDIM_XY */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0x10001); /* ffffffff BLOCKDIM_XY */
+ xf_emit(ctx, 1, 1); /* 0000ffff BLOCKDIM_Z */
+ xf_emit(ctx, 1, 0x10001); /* 00ffffff BLOCK_ALLOC */
+ xf_emit(ctx, 1, 1); /* 00000001 LANES32 */
+ xf_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */
+ xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */
+ /* SEEK */
+ xf_emit(ctx, 0x40, 0); /* ffffffff USER_PARAM */
+ switch (dev_priv->chipset) {
+ case 0x50:
+ case 0x92:
+ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */
+ xf_emit(ctx, 0x80, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 0x10*2, 0); /* ffffffff, 1f */
+ break;
+ case 0x84:
+ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */
+ xf_emit(ctx, 0x60, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 0xc*2, 0); /* ffffffff, 1f */
+ break;
+ case 0x94:
+ case 0x96:
+ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */
+ xf_emit(ctx, 0x40, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 8*2, 0); /* ffffffff, 1f */
+ break;
+ case 0x86:
+ case 0x98:
+ xf_emit(ctx, 4, 0); /* f, 0, 0, 0 */
+ xf_emit(ctx, 0x10, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 2*2, 0); /* ffffffff, 1f */
+ break;
+ case 0xa0:
+ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */
+ xf_emit(ctx, 0xf0, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 0x1e*2, 0); /* ffffffff, 1f */
+ break;
+ case 0xa3:
+ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */
+ xf_emit(ctx, 0x60, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 0xc*2, 0); /* ffffffff, 1f */
+ break;
+ case 0xa5:
+ case 0xaf:
+ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */
+ xf_emit(ctx, 0x30, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 6*2, 0); /* ffffffff, 1f */
+ break;
+ case 0xaa:
+ xf_emit(ctx, 0x12, 0);
+ break;
+ case 0xa8:
+ case 0xac:
+ xf_emit(ctx, 4, 0); /* f, 0, 0, 0 */
+ xf_emit(ctx, 0x10, 0); /* fff */
+ xf_emit(ctx, 2, 0); /* ff, fff */
+ xf_emit(ctx, 2*2, 0); /* ffffffff, 1f */
+ break;
+ }
+ xf_emit(ctx, 1, 0); /* 0000000f */
+ xf_emit(ctx, 1, 0); /* 00000000 */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 0000001f */
+ xf_emit(ctx, 4, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 00000003 turing UNK35C */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 4, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 00000003 turing UNK35C */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 000000ff */
}
static void
-nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx)
+nv50_graph_construct_gene_unk1cxx(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- /* middle of area 2 on pre-NVA0 [after m2mf], end of area 0 on NVAx */
- xf_emit(ctx, 1, 0x3f800000);
- xf_emit(ctx, 6, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0x1a);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0x12, 0);
- xf_emit(ctx, 1, 0x00ffff00);
- xf_emit(ctx, 6, 0);
- xf_emit(ctx, 1, 0xf);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 0xf, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 2, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 3);
+ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */
+ xf_emit(ctx, 1, 0x3f800000); /* ffffffff LINE_WIDTH */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1658 */
+ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_SMOOTH_ENABLE */
+ xf_emit(ctx, 3, 0); /* 00000001 POLYGON_OFFSET_*_ENABLE */
+ xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */
+ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK165C */
+ xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */
+ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */
+ xf_emit(ctx, 1, 0); /* ffffffff POLYGON_OFFSET_UNITS */
+ xf_emit(ctx, 1, 0); /* ffffffff POLYGON_OFFSET_FACTOR */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1668 */
+ xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 1, 0x11); /* 0000007f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 0000007f RT_FORMAT */
+ xf_emit(ctx, 8, 0); /* 00000001 RT_HORIZ_LINEAR */
+ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 3); /* 00000003 UNK16B4 */
else if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 2, 0x04000000);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 5);
- xf_emit(ctx, 1, 0x52);
- if (dev_priv->chipset == 0x50) {
- xf_emit(ctx, 0x13, 0);
- } else {
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 1);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0x11, 0);
- else
- xf_emit(ctx, 0x10, 0);
+ xf_emit(ctx, 1, 1); /* 00000001 UNK16B4 */
+ xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0F90 */
+ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */
+ xf_emit(ctx, 2, 0x04000000); /* 07ffffff tesla UNK0D6C */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */
+ xf_emit(ctx, 1, 5); /* 0000000f UNK1408 */
+ xf_emit(ctx, 1, 0x52); /* 000001ff SEMANTIC_PTSZ */
+ xf_emit(ctx, 1, 0); /* ffffffff POINT_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 00000007 tesla UNK0FB4 */
+ if (dev_priv->chipset != 0x50) {
+ xf_emit(ctx, 1, 0); /* 3ff */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK1110 */
}
- xf_emit(ctx, 0x10, 0x3f800000);
- xf_emit(ctx, 1, 0x10);
- xf_emit(ctx, 0x26, 0);
- xf_emit(ctx, 1, 0x8100c12);
- xf_emit(ctx, 1, 5);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 4, 0xffff);
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1928 */
+ xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */
+ xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */
+ xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 0x20, 0); /* 07ffffff VIEWPORT_HORIZ, then VIEWPORT_VERT. (W&0x3fff)<<13 | (X&0x1fff). */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK187C */
+ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */
+ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 1, 5); /* 0000000f tesla UNK1220 */
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 000000ff tesla UNK1A20 */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */
+ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */
if (dev_priv->chipset != 0x50)
- xf_emit(ctx, 1, 3);
+ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */
if (dev_priv->chipset < 0xa0)
- xf_emit(ctx, 0x1f, 0);
- else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0xc, 0);
- else
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x00ffff00);
- xf_emit(ctx, 1, 0x1a);
+ xf_emit(ctx, 0x1c, 0); /* RO */
+ else if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0x9, 0);
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */
+ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */
+ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */
+ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */
if (dev_priv->chipset != 0x50) {
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 3);
+ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */
+ xf_emit(ctx, 1, 0); /* 3ff */
}
+ /* XXX: the following block could belong either to unk1cxx, or
+ * to STRMOUT. Rather hard to tell. */
if (dev_priv->chipset < 0xa0)
- xf_emit(ctx, 0x26, 0);
+ xf_emit(ctx, 0x25, 0);
else
- xf_emit(ctx, 0x3c, 0);
- xf_emit(ctx, 1, 0x102);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 4, 4);
- if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 8, 0);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 0x3b, 0);
+}
+
+static void
+nv50_graph_construct_gene_strmout(struct nouveau_grctx *ctx)
+{
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ xf_emit(ctx, 1, 0x102); /* 0000ffff STRMOUT_BUFFER_CTRL */
+ xf_emit(ctx, 1, 0); /* ffffffff STRMOUT_PRIMITIVE_COUNT */
+ xf_emit(ctx, 4, 4); /* 000000ff STRMOUT_NUM_ATTRIBS */
+ if (dev_priv->chipset >= 0xa0) {
+ xf_emit(ctx, 4, 0); /* ffffffff UNK1A8C */
+ xf_emit(ctx, 4, 0); /* ffffffff UNK1780 */
+ }
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 1, 0x3ff);
+ xf_emit(ctx, 1, 0x3ff); /* 000003ff tesla UNK0D68 */
else
- xf_emit(ctx, 1, 0x7ff);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x102);
- xf_emit(ctx, 9, 0);
- xf_emit(ctx, 4, 4);
- xf_emit(ctx, 0x2c, 0);
+ xf_emit(ctx, 1, 0x7ff); /* 000007ff tesla UNK0D68 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ /* SEEK */
+ xf_emit(ctx, 1, 0x102); /* 0000ffff STRMOUT_BUFFER_CTRL */
+ xf_emit(ctx, 1, 0); /* ffffffff STRMOUT_PRIMITIVE_COUNT */
+ xf_emit(ctx, 4, 0); /* 000000ff STRMOUT_ADDRESS_HIGH */
+ xf_emit(ctx, 4, 0); /* ffffffff STRMOUT_ADDRESS_LOW */
+ xf_emit(ctx, 4, 4); /* 000000ff STRMOUT_NUM_ATTRIBS */
+ if (dev_priv->chipset >= 0xa0) {
+ xf_emit(ctx, 4, 0); /* ffffffff UNK1A8C */
+ xf_emit(ctx, 4, 0); /* ffffffff UNK1780 */
+ }
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_STRMOUT */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */
+ xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */
+ xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW QUERY_COUNTER */
+ xf_emit(ctx, 2, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ /* SEEK */
+ xf_emit(ctx, 0x20, 0); /* ffffffff STRMOUT_MAP */
+ xf_emit(ctx, 1, 0); /* 0000000f */
+ xf_emit(ctx, 1, 0); /* 00000000? */
+ xf_emit(ctx, 2, 0); /* ffffffff */
+}
+
+static void
+nv50_graph_construct_gene_ropm1(struct nouveau_grctx *ctx)
+{
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0D64 */
+ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0DF4 */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 0x11); /* 000000ff tesla UNK1968 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+}
+
+static void
+nv50_graph_construct_gene_ropm2(struct nouveau_grctx *ctx)
+{
+ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 2, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */
+ xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW, COUNTER */
+ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 7 */
+ /* SEEK */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */
+ xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */
+ xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW, COUNTER */
+ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0D64 */
+ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0DF4 */
+ xf_emit(ctx, 1, 0); /* 00000001 eng2d UNK260 */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 0x11); /* 000000ff tesla UNK1968 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
}
static void
@@ -1749,443 +2392,709 @@ nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx)
int magic2;
if (dev_priv->chipset == 0x50) {
magic2 = 0x00003e60;
- } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
+ } else if (!IS_NVA3F(dev_priv->chipset)) {
magic2 = 0x001ffe67;
} else {
magic2 = 0x00087e67;
}
- xf_emit(ctx, 8, 0);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, magic2);
- xf_emit(ctx, 4, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 7, 0);
- if (dev_priv->chipset >= 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 0x15);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x10);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 4, 0);
+ xf_emit(ctx, 1, 0); /* f/7 MUTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ if (dev_priv->chipset >= 0xa0 && !IS_NVAAF(dev_priv->chipset))
+ xf_emit(ctx, 1, 0x15); /* 000000ff */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */
+ xf_emit(ctx, 1, 0x10); /* 3ff/ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x92 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa0) {
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0x400);
- xf_emit(ctx, 1, 0x300);
- xf_emit(ctx, 1, 0x1001);
+ xf_emit(ctx, 3, 0); /* ff, ffffffff, ffffffff */
+ xf_emit(ctx, 1, 4); /* 7 */
+ xf_emit(ctx, 1, 0x400); /* fffffff */
+ xf_emit(ctx, 1, 0x300); /* ffff */
+ xf_emit(ctx, 1, 0x1001); /* 1fff */
if (dev_priv->chipset != 0xa0) {
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 0);
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 0); /* 0000000f UNK15C8 */
else
- xf_emit(ctx, 1, 0x15);
+ xf_emit(ctx, 1, 0x15); /* ff */
}
- xf_emit(ctx, 3, 0);
}
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 8, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x10);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0x13, 0);
- xf_emit(ctx, 1, 0x10);
- xf_emit(ctx, 0x10, 0);
- xf_emit(ctx, 0x10, 0x3f800000);
- xf_emit(ctx, 0x19, 0);
- xf_emit(ctx, 1, 0x10);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x3f);
- xf_emit(ctx, 6, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */
+ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */
+ xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000000f */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */
+ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */
+ xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 000000ff CLEAR_STENCIL */
+ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */
+ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 0x3f); /* 0000003f UNK1590 */
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 2, 0); /* ffff0ff3, ffff */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */
+ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */
if (dev_priv->chipset >= 0xa0) {
xf_emit(ctx, 2, 0);
xf_emit(ctx, 1, 0x1001);
xf_emit(ctx, 0xb, 0);
} else {
- xf_emit(ctx, 0xc, 0);
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
}
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0xf);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x11);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 4, 0);
- else
- xf_emit(ctx, 6, 0);
- xf_emit(ctx, 3, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, magic2);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 0x18, 1);
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 8, 1);
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 8, 1);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 5, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0x16, 0);
+ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f */
+ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */
+ if (dev_priv->chipset != 0x50) {
+ xf_emit(ctx, 1, 0); /* 0000000f LOGIC_OP */
+ xf_emit(ctx, 1, 0); /* 000000ff */
+ }
+ xf_emit(ctx, 1, 0); /* 00000007 OPERATION */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */
+ xf_emit(ctx, 2, 1); /* 00000007 BLEND_EQUATION_RGB, ALPHA */
+ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK12E4 */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1140 */
+ xf_emit(ctx, 2, 0); /* 00000001 */
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, 0); /* 0000000f */
+ xf_emit(ctx, 1, 0); /* 00000003 */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 2, 0); /* 00000001 */
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ } else if (dev_priv->chipset >= 0xa0) {
+ xf_emit(ctx, 2, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0); /* 00000003 */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 2, 0); /* 00000001 */
} else {
- if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 0x1b, 0);
- else
- xf_emit(ctx, 0x15, 0);
+ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1430 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
}
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 2, 1);
+ xf_emit(ctx, 4, 0); /* ffffffff CLEAR_COLOR */
+ xf_emit(ctx, 4, 0); /* ffffffff BLEND_COLOR A R G B */
+ xf_emit(ctx, 1, 0); /* 00000fff eng2d UNK2B0 */
if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 4, 0);
- else
- xf_emit(ctx, 3, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 0x10, 1);
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 0x10, 1);
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 8, 1);
- xf_emit(ctx, 3, 0);
+ xf_emit(ctx, 2, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */
+ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK19C0 */
+ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000000f LOGIC_OP */
+ if (dev_priv->chipset >= 0xa0)
+ xf_emit(ctx, 1, 0); /* 00000001 UNK12E4? NVA3+ only? */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK15C4 */
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1140 */
}
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0x5b, 0);
+ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */
+ xf_emit(ctx, 1, 0); /* 00000007 PATTERN_COLOR_FORMAT */
+ xf_emit(ctx, 2, 0); /* ffffffff PATTERN_MONO_COLOR */
+ xf_emit(ctx, 1, 0); /* 00000001 PATTERN_MONO_FORMAT */
+ xf_emit(ctx, 2, 0); /* ffffffff PATTERN_MONO_BITMAP */
+ xf_emit(ctx, 1, 0); /* 00000003 PATTERN_SELECT */
+ xf_emit(ctx, 1, 0); /* 000000ff ROP */
+ xf_emit(ctx, 1, 0); /* ffffffff BETA1 */
+ xf_emit(ctx, 1, 0); /* ffffffff BETA4 */
+ xf_emit(ctx, 1, 0); /* 00000007 OPERATION */
+ xf_emit(ctx, 0x50, 0); /* 10x ffffff, ffffff, ffffff, ffffff, 3 PATTERN */
}
static void
-nv50_graph_construct_xfer_tp_x1(struct nouveau_grctx *ctx)
+nv50_graph_construct_xfer_unk84xx(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
int magic3;
- if (dev_priv->chipset == 0x50)
+ switch (dev_priv->chipset) {
+ case 0x50:
magic3 = 0x1000;
- else if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8)
+ break;
+ case 0x86:
+ case 0x98:
+ case 0xa8:
+ case 0xaa:
+ case 0xac:
+ case 0xaf:
magic3 = 0x1e00;
- else
+ break;
+ default:
magic3 = 0;
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 4);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0x24, 0);
+ }
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 7f/ff[NVA0+] VP_REG_ALLOC_RESULT */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 0); /* 111/113[NVA0+] */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0x1f, 0); /* ffffffff */
else if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 0x14, 0);
+ xf_emit(ctx, 0x0f, 0); /* ffffffff */
else
- xf_emit(ctx, 0x15, 0);
- xf_emit(ctx, 2, 4);
+ xf_emit(ctx, 0x10, 0); /* fffffff VP_RESULT_MAP_1 up */
+ xf_emit(ctx, 2, 0); /* f/1f[NVA3], fffffff/ffffffff[NVA0+] */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */
if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 1, 0x03020100);
+ xf_emit(ctx, 1, 0x03020100); /* ffffffff */
else
- xf_emit(ctx, 1, 0x00608080);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 2, 4);
- xf_emit(ctx, 1, 0x80);
+ xf_emit(ctx, 1, 0x00608080); /* fffffff VP_RESULT_MAP_0 */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 2, 0); /* 111/113, 7f/ff */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */
if (magic3)
- xf_emit(ctx, 1, magic3);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 0x24, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0x80);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0x03020100);
- xf_emit(ctx, 1, 3);
+ xf_emit(ctx, 1, magic3); /* 00007fff tesla UNK141C */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 0); /* 111/113 */
+ xf_emit(ctx, 0x1f, 0); /* ffffffff GP_RESULT_MAP_1 up */
+ xf_emit(ctx, 1, 0); /* 0000001f */
+ xf_emit(ctx, 1, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */
+ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0x03020100); /* ffffffff GP_RESULT_MAP_0 */
+ xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */
if (magic3)
- xf_emit(ctx, 1, magic3);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 3);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 4);
+ xf_emit(ctx, 1, magic3); /* 7fff tesla UNK141C */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 0); /* 111/113 */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */
+ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK13A0 */
+ xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ xf_emit(ctx, 1, 0); /* 111/113 */
if (dev_priv->chipset == 0x94 || dev_priv->chipset == 0x96)
- xf_emit(ctx, 0x1024, 0);
+ xf_emit(ctx, 0x1020, 0); /* 4 x (0x400 x 0xffffffff, ff, 0, 0, 0, 4 x ffffffff) */
else if (dev_priv->chipset < 0xa0)
- xf_emit(ctx, 0xa24, 0);
- else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa)
- xf_emit(ctx, 0x214, 0);
+ xf_emit(ctx, 0xa20, 0); /* 4 x (0x280 x 0xffffffff, ff, 0, 0, 0, 4 x ffffffff) */
+ else if (!IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0x210, 0); /* ffffffff */
else
- xf_emit(ctx, 0x414, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 3);
- xf_emit(ctx, 2, 0);
+ xf_emit(ctx, 0x410, 0); /* ffffffff */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */
+ xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */
+ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
}
static void
-nv50_graph_construct_xfer_tp_x2(struct nouveau_grctx *ctx)
+nv50_graph_construct_xfer_tprop(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
int magic1, magic2;
if (dev_priv->chipset == 0x50) {
magic1 = 0x3ff;
magic2 = 0x00003e60;
- } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
+ } else if (!IS_NVA3F(dev_priv->chipset)) {
magic1 = 0x7ff;
magic2 = 0x001ffe67;
} else {
magic1 = 0x7ff;
magic2 = 0x00087e67;
}
- xf_emit(ctx, 3, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0xc, 0);
- xf_emit(ctx, 1, 0xf);
- xf_emit(ctx, 0xb, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 4, 0xffff);
- xf_emit(ctx, 8, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 5, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 1, 3);
- xf_emit(ctx, 1, 0);
- } else if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0xa, 0);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 1, 2);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 0x18, 1);
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 8, 1);
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 8, 1);
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* ffffffff ALPHA_TEST_REF */
+ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000000f UNK16A0 */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 4, 0); /* ffffffff BLEND_COLOR */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK19C0 */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK0FDC */
+ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */
+ xf_emit(ctx, 1, 0); /* ff[NV50]/3ff[NV84+] */
+ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */
+ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */
+ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */
+ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */
+ xf_emit(ctx, 1, 0); /* 7 */
+ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff COLOR_KEY */
+ xf_emit(ctx, 1, 0); /* 00000001 COLOR_KEY_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 COLOR_KEY_FORMAT */
+ xf_emit(ctx, 2, 0); /* ffffffff SIFC_BITMAP_COLOR */
+ xf_emit(ctx, 1, 1); /* 00000001 SIFC_BITMAP_WRITE_BIT0_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK16B4 */
+ xf_emit(ctx, 1, 0); /* 00000003 */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1298 */
+ } else if (dev_priv->chipset >= 0xa0) {
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK16B4 */
+ xf_emit(ctx, 1, 0); /* 00000003 */
+ } else {
+ xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */
}
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 3, 0xcf);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0xa, 0);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 8, 1);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 1, 0xf);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, magic2);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x11);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 2, 1);
- else
- xf_emit(ctx, 1, 1);
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 0); /* 00000001 UNK12E4 */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_SRC_RGB */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_DST_RGB */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_SRC_ALPHA */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_DST_ALPHA */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */
+ }
+ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */
+ xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */
+ xf_emit(ctx, 1, 0); /* 7 */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */
+ xf_emit(ctx, 1, 0); /* 00000007 OPERATION */
+ xf_emit(ctx, 1, 0xcf); /* 000000ff SIFC_FORMAT */
+ xf_emit(ctx, 1, 0xcf); /* 000000ff DRAW_COLOR_FORMAT */
+ xf_emit(ctx, 1, 0xcf); /* 000000ff SRC_FORMAT */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+ xf_emit(ctx, 1, 0); /* 7/f[NVA3] MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */
+ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 8, 1); /* 00000001 UNK19E0 */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
if(dev_priv->chipset == 0x50)
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 1, 0); /* ff */
else
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 5, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, magic1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 0x28, 0);
- xf_emit(ctx, 8, 8);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 8, 0x400);
- xf_emit(ctx, 8, 0x300);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0xf);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x20);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 0x100);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x40);
- xf_emit(ctx, 1, 0x100);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 3);
- xf_emit(ctx, 4, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, magic2);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 9, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x400);
- xf_emit(ctx, 1, 0x300);
- xf_emit(ctx, 1, 0x1001);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 4, 0);
- else
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 1, 0xf);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 0x15, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 3, 0);
- } else
- xf_emit(ctx, 0x17, 0);
+ xf_emit(ctx, 3, 0); /* 1, 7, 3ff */
+ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */
+ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DU_DX_FRACT */
+ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DU_DX_INT */
+ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DV_DY_FRACT */
+ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DV_DY_INT */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, magic1); /* 3ff/7ff tesla UNK0D68 */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 8, 0); /* 0000ffff DMA_COLOR */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_GLOBAL */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_LOCAL */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_STACK */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_DST */
+ xf_emit(ctx, 1, 0); /* 7 */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 8, 0); /* 000000ff RT_ADDRESS_HIGH */
+ xf_emit(ctx, 8, 0); /* ffffffff RT_LAYER_STRIDE */
+ xf_emit(ctx, 8, 0); /* ffffffff RT_ADDRESS_LOW */
+ xf_emit(ctx, 8, 8); /* 0000007f RT_TILE_MODE */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 8, 0x400); /* 0fffffff RT_HORIZ */
+ xf_emit(ctx, 8, 0x300); /* 0000ffff RT_VERT */
+ xf_emit(ctx, 1, 1); /* 00001fff RT_ARRAY_MODE */
+ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 1, 0x20); /* 00000fff DST_TILE_MODE */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */
+ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_HEIGHT */
+ xf_emit(ctx, 1, 0); /* 000007ff DST_LAYER */
+ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */
+ xf_emit(ctx, 1, 0); /* ffffffff DST_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0); /* 000000ff DST_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0x40); /* 0007ffff DST_PITCH */
+ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_WIDTH */
+ xf_emit(ctx, 1, 0); /* 0000ffff */
+ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK15AC */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */
+ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_ZETA */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 2, 0); /* ffff, ff/3ff */
+ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0); /* ffffffff ZETA_LAYER_STRIDE */
+ xf_emit(ctx, 1, 0); /* 000000ff ZETA_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff ZETA_ADDRESS_LOW */
+ xf_emit(ctx, 1, 4); /* 00000007 ZETA_TILE_MODE */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ xf_emit(ctx, 1, 0x400); /* 0fffffff ZETA_HORIZ */
+ xf_emit(ctx, 1, 0x300); /* 0000ffff ZETA_VERT */
+ xf_emit(ctx, 1, 0x1001); /* 00001fff ZETA_ARRAY_MODE */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 0); /* 00000001 */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */
+ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */
+ xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */
+ xf_emit(ctx, 1, 0); /* 7 */
+ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ }
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 1, 0x0fac6881);
- xf_emit(ctx, 1, magic2);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 3, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 2, 1);
- else
- xf_emit(ctx, 1, 1);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 2, 0);
- else if (dev_priv->chipset != 0x50)
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 1, 0x0fac6881); /* fffffff */
+ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */
+ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */
+ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */
+ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, 0); /* 0000000f tesla UNK15C8 */
+ }
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */
+ if (dev_priv->chipset >= 0xa0) {
+ xf_emit(ctx, 3, 0); /* 7/f, 1, ffff0ff3 */
+ xf_emit(ctx, 1, 0xfac6881); /* fffffff */
+ xf_emit(ctx, 4, 0); /* 1, 1, 1, 3ff */
+ xf_emit(ctx, 1, 4); /* 7 */
+ xf_emit(ctx, 1, 0); /* 1 */
+ xf_emit(ctx, 2, 1); /* 1 */
+ xf_emit(ctx, 2, 0); /* 7, f */
+ xf_emit(ctx, 1, 1); /* 1 */
+ xf_emit(ctx, 1, 0); /* 7/f */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 0x9, 0); /* 1 */
+ else
+ xf_emit(ctx, 0x8, 0); /* 1 */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 8, 1); /* 1 */
+ xf_emit(ctx, 1, 0x11); /* 7f */
+ xf_emit(ctx, 7, 0); /* 7f */
+ xf_emit(ctx, 1, 0xfac6881); /* fffffff */
+ xf_emit(ctx, 1, 0xf); /* f */
+ xf_emit(ctx, 7, 0); /* f */
+ xf_emit(ctx, 1, 0x11); /* 7f */
+ xf_emit(ctx, 1, 1); /* 1 */
+ xf_emit(ctx, 5, 0); /* 1, 7, 3ff, 3, 7 */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ }
+ }
}
static void
-nv50_graph_construct_xfer_tp_x3(struct nouveau_grctx *ctx)
+nv50_graph_construct_xfer_tex(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
+ xf_emit(ctx, 2, 0); /* 1 LINKED_TSC. yes, 2. */
+ if (dev_priv->chipset != 0x50)
+ xf_emit(ctx, 1, 0); /* 3 */
+ xf_emit(ctx, 1, 1); /* 1ffff BLIT_DU_DX_INT */
+ xf_emit(ctx, 1, 0); /* fffff BLIT_DU_DX_FRACT */
+ xf_emit(ctx, 1, 1); /* 1ffff BLIT_DV_DY_INT */
+ xf_emit(ctx, 1, 0); /* fffff BLIT_DV_DY_FRACT */
if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 2, 0);
+ xf_emit(ctx, 1, 0); /* 3 BLIT_CONTROL */
else
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0x2a712488);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x4085c000);
- xf_emit(ctx, 1, 0x40);
- xf_emit(ctx, 1, 0x100);
- xf_emit(ctx, 1, 0x10100);
- xf_emit(ctx, 1, 0x02800000);
+ xf_emit(ctx, 2, 0); /* 3ff, 1 */
+ xf_emit(ctx, 1, 0x2a712488); /* ffffffff SRC_TIC_0 */
+ xf_emit(ctx, 1, 0); /* ffffffff SRC_TIC_1 */
+ xf_emit(ctx, 1, 0x4085c000); /* ffffffff SRC_TIC_2 */
+ xf_emit(ctx, 1, 0x40); /* ffffffff SRC_TIC_3 */
+ xf_emit(ctx, 1, 0x100); /* ffffffff SRC_TIC_4 */
+ xf_emit(ctx, 1, 0x10100); /* ffffffff SRC_TIC_5 */
+ xf_emit(ctx, 1, 0x02800000); /* ffffffff SRC_TIC_6 */
+ xf_emit(ctx, 1, 0); /* ffffffff SRC_TIC_7 */
+ if (dev_priv->chipset == 0x50) {
+ xf_emit(ctx, 1, 0); /* 00000001 turing UNK358 */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34? */
+ xf_emit(ctx, 1, 0); /* 00000003 turing UNK37C tesla UNK1690 */
+ xf_emit(ctx, 1, 0); /* 00000003 BLIT_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000001 turing UNK32C tesla UNK0F94 */
+ } else if (!IS_NVAAF(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34? */
+ xf_emit(ctx, 1, 0); /* 00000003 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ xf_emit(ctx, 1, 0); /* 00000003 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1664 / turing UNK03E8 */
+ xf_emit(ctx, 1, 0); /* 00000003 */
+ xf_emit(ctx, 1, 0); /* 000003ff */
+ } else {
+ xf_emit(ctx, 0x6, 0);
+ }
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34 */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_TEXTURE */
+ xf_emit(ctx, 1, 0); /* 0000ffff DMA_SRC */
}
static void
-nv50_graph_construct_xfer_tp_x4(struct nouveau_grctx *ctx)
+nv50_graph_construct_xfer_unk8cxx(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- xf_emit(ctx, 2, 0x04e3bfdf);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x00ffff00);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 2, 1);
- else
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 0x00ffff00);
- xf_emit(ctx, 8, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0x30201000);
- xf_emit(ctx, 1, 0x70605040);
- xf_emit(ctx, 1, 0xb8a89888);
- xf_emit(ctx, 1, 0xf8e8d8c8);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x1a);
-}
-
-static void
-nv50_graph_construct_xfer_tp_x5(struct nouveau_grctx *ctx)
-{
- struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 0xfac6881);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 2, 0);
- xf_emit(ctx, 1, 1);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0xb, 0);
- else
- xf_emit(ctx, 0xa, 0);
- xf_emit(ctx, 8, 1);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0xfac6881);
- xf_emit(ctx, 1, 0xf);
- xf_emit(ctx, 7, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 1);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 6, 0);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 6, 0);
- } else {
- xf_emit(ctx, 0xb, 0);
- }
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 2, 0); /* 7, ffff0ff3 */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE */
+ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0D64 */
+ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0DF4 */
+ xf_emit(ctx, 1, 1); /* 00000001 UNK15B4 */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */
+ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK0F98 */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1668 */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */
+ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */
+ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_SMOOTH_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1658 */
+ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */
+ xf_emit(ctx, 1, 0); /* ffff0ff3 */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE */
+ xf_emit(ctx, 1, 1); /* 00000001 UNK15B4 */
+ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */
+ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK165C */
+ xf_emit(ctx, 1, 0x30201000); /* ffffffff tesla UNK1670 */
+ xf_emit(ctx, 1, 0x70605040); /* ffffffff tesla UNK1670 */
+ xf_emit(ctx, 1, 0xb8a89888); /* ffffffff tesla UNK1670 */
+ xf_emit(ctx, 1, 0xf8e8d8c8); /* ffffffff tesla UNK1670 */
+ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */
+ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */
}
static void
@@ -2193,108 +3102,136 @@ nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
if (dev_priv->chipset < 0xa0) {
- nv50_graph_construct_xfer_tp_x1(ctx);
- nv50_graph_construct_xfer_tp_x2(ctx);
- nv50_graph_construct_xfer_tp_x3(ctx);
- if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 0xf, 0);
- else
- xf_emit(ctx, 0x12, 0);
- nv50_graph_construct_xfer_tp_x4(ctx);
+ nv50_graph_construct_xfer_unk84xx(ctx);
+ nv50_graph_construct_xfer_tprop(ctx);
+ nv50_graph_construct_xfer_tex(ctx);
+ nv50_graph_construct_xfer_unk8cxx(ctx);
} else {
- nv50_graph_construct_xfer_tp_x3(ctx);
- if (dev_priv->chipset < 0xaa)
- xf_emit(ctx, 0xc, 0);
- else
- xf_emit(ctx, 0xa, 0);
- nv50_graph_construct_xfer_tp_x2(ctx);
- nv50_graph_construct_xfer_tp_x5(ctx);
- nv50_graph_construct_xfer_tp_x4(ctx);
- nv50_graph_construct_xfer_tp_x1(ctx);
+ nv50_graph_construct_xfer_tex(ctx);
+ nv50_graph_construct_xfer_tprop(ctx);
+ nv50_graph_construct_xfer_unk8cxx(ctx);
+ nv50_graph_construct_xfer_unk84xx(ctx);
}
}
static void
-nv50_graph_construct_xfer_tp2(struct nouveau_grctx *ctx)
+nv50_graph_construct_xfer_mpc(struct nouveau_grctx *ctx)
{
struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
- int i, mpcnt;
- if (dev_priv->chipset == 0x98 || dev_priv->chipset == 0xaa)
- mpcnt = 1;
- else if (dev_priv->chipset < 0xa0 || dev_priv->chipset >= 0xa8)
- mpcnt = 2;
- else
- mpcnt = 3;
+ int i, mpcnt = 2;
+ switch (dev_priv->chipset) {
+ case 0x98:
+ case 0xaa:
+ mpcnt = 1;
+ break;
+ case 0x50:
+ case 0x84:
+ case 0x86:
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ case 0xa8:
+ case 0xac:
+ mpcnt = 2;
+ break;
+ case 0xa0:
+ case 0xa3:
+ case 0xa5:
+ case 0xaf:
+ mpcnt = 3;
+ break;
+ }
for (i = 0; i < mpcnt; i++) {
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x80);
- xf_emit(ctx, 1, 0x80007004);
- xf_emit(ctx, 1, 0x04000400);
+ xf_emit(ctx, 1, 0); /* ff */
+ xf_emit(ctx, 1, 0x80); /* ffffffff tesla UNK1404 */
+ xf_emit(ctx, 1, 0x80007004); /* ffffffff tesla UNK12B0 */
+ xf_emit(ctx, 1, 0x04000400); /* ffffffff */
if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 1, 0xc0);
- xf_emit(ctx, 1, 0x1000);
- xf_emit(ctx, 2, 0);
- if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8) {
- xf_emit(ctx, 1, 0xe00);
- xf_emit(ctx, 1, 0x1e00);
+ xf_emit(ctx, 1, 0xc0); /* 00007fff tesla UNK152C */
+ xf_emit(ctx, 1, 0x1000); /* 0000ffff tesla UNK0D60 */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */
+ if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset == 0xa8 || IS_NVAAF(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 0xe00); /* 7fff */
+ xf_emit(ctx, 1, 0x1e00); /* 7fff */
}
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0);
+ xf_emit(ctx, 1, 1); /* 000000ff VP_REG_ALLOC_TEMP */
+ xf_emit(ctx, 1, 0); /* 00000001 LINKED_TSC */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
if (dev_priv->chipset == 0x50)
- xf_emit(ctx, 2, 0x1000);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 2);
- if (dev_priv->chipset >= 0xaa)
- xf_emit(ctx, 0xb, 0);
+ xf_emit(ctx, 2, 0x1000); /* 7fff tesla UNK141C */
+ xf_emit(ctx, 1, 1); /* 000000ff GP_REG_ALLOC_TEMP */
+ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */
+ xf_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */
+ xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */
+ if (IS_NVAAF(dev_priv->chipset))
+ xf_emit(ctx, 0xb, 0); /* RO */
else if (dev_priv->chipset >= 0xa0)
- xf_emit(ctx, 0xc, 0);
+ xf_emit(ctx, 0xc, 0); /* RO */
else
- xf_emit(ctx, 0xa, 0);
+ xf_emit(ctx, 0xa, 0); /* RO */
}
- xf_emit(ctx, 1, 0x08100c12);
- xf_emit(ctx, 1, 0);
+ xf_emit(ctx, 1, 0x08100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
if (dev_priv->chipset >= 0xa0) {
- xf_emit(ctx, 1, 0x1fe21);
+ xf_emit(ctx, 1, 0x1fe21); /* 0003ffff tesla UNK0FAC */
}
- xf_emit(ctx, 5, 0);
- xf_emit(ctx, 4, 0xffff);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 2, 0x10001);
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 0x1fe21);
- xf_emit(ctx, 1, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 1);
- xf_emit(ctx, 4, 0);
- xf_emit(ctx, 1, 0x08100c12);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 8, 0);
- xf_emit(ctx, 1, 0xfac6881);
- xf_emit(ctx, 1, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
- xf_emit(ctx, 1, 3);
- xf_emit(ctx, 3, 0);
- xf_emit(ctx, 1, 4);
- xf_emit(ctx, 9, 0);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 2, 1);
- xf_emit(ctx, 1, 2);
- xf_emit(ctx, 3, 1);
- xf_emit(ctx, 1, 0);
- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 0x10, 1);
- xf_emit(ctx, 8, 2);
- xf_emit(ctx, 0x18, 1);
- xf_emit(ctx, 3, 0);
+ xf_emit(ctx, 3, 0); /* 7fff, 0, 0 */
+ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */
+ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */
+ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */
+ xf_emit(ctx, 1, 1); /* 00000001 LANES32 */
+ xf_emit(ctx, 1, 0x10001); /* 00ffffff BLOCK_ALLOC */
+ xf_emit(ctx, 1, 0x10001); /* ffffffff BLOCKDIM_XY */
+ xf_emit(ctx, 1, 1); /* 0000ffff BLOCKDIM_Z */
+ xf_emit(ctx, 1, 0); /* ffffffff SHARED_SIZE */
+ xf_emit(ctx, 1, 0x1fe21); /* 1ffff/3ffff[NVA0+] tesla UNk0FAC */
+ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34 */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
+ xf_emit(ctx, 1, 0); /* ff/3ff */
+ xf_emit(ctx, 1, 0); /* 1 LINKED_TSC */
+ xf_emit(ctx, 1, 0); /* ff FP_ADDRESS_HIGH */
+ xf_emit(ctx, 1, 0); /* ffffffff FP_ADDRESS_LOW */
+ xf_emit(ctx, 1, 0x08100c12); /* 1fffffff FP_INTERPOLANT_CTRL */
+ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */
+ xf_emit(ctx, 1, 0); /* 000000ff FRAG_COLOR_CLAMP_EN */
+ xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */
+ xf_emit(ctx, 1, 0x11); /* 0000007f RT_FORMAT */
+ xf_emit(ctx, 7, 0); /* 0000007f RT_FORMAT */
+ xf_emit(ctx, 1, 0); /* 00000007 */
+ xf_emit(ctx, 1, 0xfac6881); /* 0fffffff RT_CONTROL */
+ xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */
+ if (IS_NVA3F(dev_priv->chipset))
+ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK16B4 */
+ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */
+ xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */
+ xf_emit(ctx, 1, 4); /* ffffffff tesla UNK1400 */
+ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */
+ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */
+ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */
+ if (IS_NVA3F(dev_priv->chipset)) {
+ xf_emit(ctx, 1, 0); /* 00000001 UNK12E4 */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */
+ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */
+ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */
+ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */
+ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1928 */
+ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */
}
- xf_emit(ctx, 1, 4);
+ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0F90 */
+ xf_emit(ctx, 1, 4); /* 000000ff FP_RESULT_COUNT */
+ /* XXX: demagic this part some day */
if (dev_priv->chipset == 0x50)
xf_emit(ctx, 0x3a0, 0);
else if (dev_priv->chipset < 0x94)
@@ -2303,9 +3240,9 @@ nv50_graph_construct_xfer_tp2(struct nouveau_grctx *ctx)
xf_emit(ctx, 0x39f, 0);
else
xf_emit(ctx, 0x3a3, 0);
- xf_emit(ctx, 1, 0x11);
- xf_emit(ctx, 1, 0);
- xf_emit(ctx, 1, 1);
+ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */
+ xf_emit(ctx, 1, 0); /* 7 OPERATION */
+ xf_emit(ctx, 1, 1); /* 1 DST_LINEAR */
xf_emit(ctx, 0x2d, 0);
}
@@ -2323,52 +3260,56 @@ nv50_graph_construct_xfer2(struct nouveau_grctx *ctx)
if (dev_priv->chipset < 0xa0) {
for (i = 0; i < 8; i++) {
ctx->ctxvals_pos = offset + i;
+ /* that little bugger belongs to csched. No idea
+ * what it's doing here. */
if (i == 0)
- xf_emit(ctx, 1, 0x08100c12);
+ xf_emit(ctx, 1, 0x08100c12); /* FP_INTERPOLANT_CTRL */
if (units & (1 << i))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
}
} else {
/* Strand 0: TPs 0, 1 */
ctx->ctxvals_pos = offset;
- xf_emit(ctx, 1, 0x08100c12);
+ /* that little bugger belongs to csched. No idea
+ * what it's doing here. */
+ xf_emit(ctx, 1, 0x08100c12); /* FP_INTERPOLANT_CTRL */
if (units & (1 << 0))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if (units & (1 << 1))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
- /* Strand 0: TPs 2, 3 */
+ /* Strand 1: TPs 2, 3 */
ctx->ctxvals_pos = offset + 1;
if (units & (1 << 2))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if (units & (1 << 3))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
- /* Strand 0: TPs 4, 5, 6 */
+ /* Strand 2: TPs 4, 5, 6 */
ctx->ctxvals_pos = offset + 2;
if (units & (1 << 4))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if (units & (1 << 5))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if (units & (1 << 6))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
- /* Strand 0: TPs 7, 8, 9 */
+ /* Strand 3: TPs 7, 8, 9 */
ctx->ctxvals_pos = offset + 3;
if (units & (1 << 7))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if (units & (1 << 8))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if (units & (1 << 9))
- nv50_graph_construct_xfer_tp2(ctx);
+ nv50_graph_construct_xfer_mpc(ctx);
if ((ctx->ctxvals_pos-offset)/8 > size)
size = (ctx->ctxvals_pos-offset)/8;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
index 91ef93cf1f35..a53fc974332b 100644
--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -32,39 +32,87 @@
struct nv50_instmem_priv {
uint32_t save1700[5]; /* 0x1700->0x1710 */
- struct nouveau_gpuobj_ref *pramin_pt;
- struct nouveau_gpuobj_ref *pramin_bar;
- struct nouveau_gpuobj_ref *fb_bar;
+ struct nouveau_gpuobj *pramin_pt;
+ struct nouveau_gpuobj *pramin_bar;
+ struct nouveau_gpuobj *fb_bar;
};
-#define NV50_INSTMEM_PAGE_SHIFT 12
-#define NV50_INSTMEM_PAGE_SIZE (1 << NV50_INSTMEM_PAGE_SHIFT)
-#define NV50_INSTMEM_PT_SIZE(a) (((a) >> 12) << 3)
+static void
+nv50_channel_del(struct nouveau_channel **pchan)
+{
+ struct nouveau_channel *chan;
-/*NOTE: - Assumes 0x1700 already covers the correct MiB of PRAMIN
- */
-#define BAR0_WI32(g, o, v) do { \
- uint32_t offset; \
- if ((g)->im_backing) { \
- offset = (g)->im_backing_start; \
- } else { \
- offset = chan->ramin->gpuobj->im_backing_start; \
- offset += (g)->im_pramin->start; \
- } \
- offset += (o); \
- nv_wr32(dev, NV_RAMIN + (offset & 0xfffff), (v)); \
-} while (0)
+ chan = *pchan;
+ *pchan = NULL;
+ if (!chan)
+ return;
+
+ nouveau_gpuobj_ref(NULL, &chan->ramfc);
+ nouveau_gpuobj_ref(NULL, &chan->vm_pd);
+ if (chan->ramin_heap.free_stack.next)
+ drm_mm_takedown(&chan->ramin_heap);
+ nouveau_gpuobj_ref(NULL, &chan->ramin);
+ kfree(chan);
+}
+
+static int
+nv50_channel_new(struct drm_device *dev, u32 size,
+ struct nouveau_channel **pchan)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 pgd = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200;
+ u32 fc = (dev_priv->chipset == 0x50) ? 0x0000 : 0x4200;
+ struct nouveau_channel *chan;
+ int ret;
+
+ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+ chan->dev = dev;
+
+ ret = nouveau_gpuobj_new(dev, NULL, size, 0x1000, 0, &chan->ramin);
+ if (ret) {
+ nv50_channel_del(&chan);
+ return ret;
+ }
+
+ ret = drm_mm_init(&chan->ramin_heap, 0x6000, chan->ramin->size);
+ if (ret) {
+ nv50_channel_del(&chan);
+ return ret;
+ }
+
+ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst == ~0 ? ~0 :
+ chan->ramin->pinst + pgd,
+ chan->ramin->vinst + pgd,
+ 0x4000, NVOBJ_FLAG_ZERO_ALLOC,
+ &chan->vm_pd);
+ if (ret) {
+ nv50_channel_del(&chan);
+ return ret;
+ }
+
+ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst == ~0 ? ~0 :
+ chan->ramin->pinst + fc,
+ chan->ramin->vinst + fc, 0x100,
+ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramfc);
+ if (ret) {
+ nv50_channel_del(&chan);
+ return ret;
+ }
+
+ *pchan = chan;
+ return 0;
+}
int
nv50_instmem_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_channel *chan;
- uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size;
- uint32_t save_nv001700;
- uint64_t v;
struct nv50_instmem_priv *priv;
+ struct nouveau_channel *chan;
int ret, i;
+ u32 tmp;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -75,212 +123,115 @@ nv50_instmem_init(struct drm_device *dev)
for (i = 0x1700; i <= 0x1710; i += 4)
priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
- /* Reserve the last MiB of VRAM, we should probably try to avoid
- * setting up the below tables over the top of the VBIOS image at
- * some point.
- */
- dev_priv->ramin_rsvd_vram = 1 << 20;
- c_offset = dev_priv->vram_size - dev_priv->ramin_rsvd_vram;
- c_size = 128 << 10;
- c_vmpd = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200;
- c_ramfc = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20;
- c_base = c_vmpd + 0x4000;
- pt_size = NV50_INSTMEM_PT_SIZE(dev_priv->ramin_size);
-
- NV_DEBUG(dev, " Rsvd VRAM base: 0x%08x\n", c_offset);
- NV_DEBUG(dev, " VBIOS image: 0x%08x\n",
- (nv_rd32(dev, 0x619f04) & ~0xff) << 8);
- NV_DEBUG(dev, " Aperture size: %d MiB\n", dev_priv->ramin_size >> 20);
- NV_DEBUG(dev, " PT size: %d KiB\n", pt_size >> 10);
-
- /* Determine VM layout, we need to do this first to make sure
- * we allocate enough memory for all the page tables.
- */
- dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
- dev_priv->vm_gart_size = NV50_VM_BLOCK;
-
- dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
- dev_priv->vm_vram_size = dev_priv->vram_size;
- if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
- dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
- dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
- dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
-
- dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
-
- NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
- dev_priv->vm_gart_base,
- dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
- NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
- dev_priv->vm_vram_base,
- dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
-
- c_size += dev_priv->vm_vram_pt_nr * (NV50_VM_BLOCK / 65536 * 8);
-
- /* Map BAR0 PRAMIN aperture over the memory we want to use */
- save_nv001700 = nv_rd32(dev, NV50_PUNK_BAR0_PRAMIN);
- nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (c_offset >> 16));
-
- /* Create a fake channel, and use it as our "dummy" channels 0/127.
- * The main reason for creating a channel is so we can use the gpuobj
- * code. However, it's probably worth noting that NVIDIA also setup
- * their channels 0/127 with the same values they configure here.
- * So, there may be some other reason for doing this.
- *
- * Have to create the entire channel manually, as the real channel
- * creation code assumes we have PRAMIN access, and we don't until
- * we're done here.
- */
- chan = kzalloc(sizeof(*chan), GFP_KERNEL);
- if (!chan)
+ /* Global PRAMIN heap */
+ ret = drm_mm_init(&dev_priv->ramin_heap, 0, dev_priv->ramin_size);
+ if (ret) {
+ NV_ERROR(dev, "Failed to init RAMIN heap\n");
return -ENOMEM;
- chan->id = 0;
- chan->dev = dev;
- chan->file_priv = (struct drm_file *)-2;
- dev_priv->fifos[0] = dev_priv->fifos[127] = chan;
-
- INIT_LIST_HEAD(&chan->ramht_refs);
+ }
- /* Channel's PRAMIN object + heap */
- ret = nouveau_gpuobj_new_fake(dev, 0, c_offset, c_size, 0,
- NULL, &chan->ramin);
+ /* we need a channel to plug into the hw to control the BARs */
+ ret = nv50_channel_new(dev, 128*1024, &dev_priv->fifos[0]);
if (ret)
return ret;
+ chan = dev_priv->fifos[127] = dev_priv->fifos[0];
- if (drm_mm_init(&chan->ramin_heap, c_base, c_size - c_base))
- return -ENOMEM;
-
- /* RAMFC + zero channel's PRAMIN up to start of VM pagedir */
- ret = nouveau_gpuobj_new_fake(dev, c_ramfc, c_offset + c_ramfc,
- 0x4000, 0, NULL, &chan->ramfc);
+ /* allocate page table for PRAMIN BAR */
+ ret = nouveau_gpuobj_new(dev, chan, (dev_priv->ramin_size >> 12) * 8,
+ 0x1000, NVOBJ_FLAG_ZERO_ALLOC,
+ &priv->pramin_pt);
if (ret)
return ret;
- for (i = 0; i < c_vmpd; i += 4)
- BAR0_WI32(chan->ramin->gpuobj, i, 0);
+ nv_wo32(chan->vm_pd, 0x0000, priv->pramin_pt->vinst | 0x63);
+ nv_wo32(chan->vm_pd, 0x0004, 0);
- /* VM page directory */
- ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd,
- 0x4000, 0, &chan->vm_pd, NULL);
+ /* DMA object for PRAMIN BAR */
+ ret = nouveau_gpuobj_new(dev, chan, 6*4, 16, 0, &priv->pramin_bar);
if (ret)
return ret;
- for (i = 0; i < 0x4000; i += 8) {
- BAR0_WI32(chan->vm_pd, i + 0x00, 0x00000000);
- BAR0_WI32(chan->vm_pd, i + 0x04, 0x00000000);
- }
-
- /* PRAMIN page table, cheat and map into VM at 0x0000000000.
- * We map the entire fake channel into the start of the PRAMIN BAR
- */
- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
- 0, &priv->pramin_pt);
+ nv_wo32(priv->pramin_bar, 0x00, 0x7fc00000);
+ nv_wo32(priv->pramin_bar, 0x04, dev_priv->ramin_size - 1);
+ nv_wo32(priv->pramin_bar, 0x08, 0x00000000);
+ nv_wo32(priv->pramin_bar, 0x0c, 0x00000000);
+ nv_wo32(priv->pramin_bar, 0x10, 0x00000000);
+ nv_wo32(priv->pramin_bar, 0x14, 0x00000000);
+
+ /* map channel into PRAMIN, gpuobj didn't do it for us */
+ ret = nv50_instmem_bind(dev, chan->ramin);
if (ret)
return ret;
- v = c_offset | 1;
- if (dev_priv->vram_sys_base) {
- v += dev_priv->vram_sys_base;
- v |= 0x30;
- }
+ /* poke regs... */
+ nv_wr32(dev, 0x001704, 0x00000000 | (chan->ramin->vinst >> 12));
+ nv_wr32(dev, 0x001704, 0x40000000 | (chan->ramin->vinst >> 12));
+ nv_wr32(dev, 0x00170c, 0x80000000 | (priv->pramin_bar->cinst >> 4));
- i = 0;
- while (v < dev_priv->vram_sys_base + c_offset + c_size) {
- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, lower_32_bits(v));
- BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, upper_32_bits(v));
- v += 0x1000;
- i += 8;
+ tmp = nv_ri32(dev, 0);
+ nv_wi32(dev, 0, ~tmp);
+ if (nv_ri32(dev, 0) != ~tmp) {
+ NV_ERROR(dev, "PRAMIN readback failed\n");
+ return -EIO;
}
+ nv_wi32(dev, 0, tmp);
- while (i < pt_size) {
- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000000);
- BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
- i += 8;
- }
+ dev_priv->ramin_available = true;
- BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
- BAR0_WI32(chan->vm_pd, 0x04, 0x00000000);
+ /* Determine VM layout */
+ dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
+ dev_priv->vm_gart_size = NV50_VM_BLOCK;
+
+ dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
+ dev_priv->vm_vram_size = dev_priv->vram_size;
+ if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
+ dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
+ dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
+ dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
+
+ dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
+
+ NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
+ dev_priv->vm_gart_base,
+ dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
+ NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
+ dev_priv->vm_vram_base,
+ dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
/* VRAM page table(s), mapped into VM at +1GiB */
for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0,
- NV50_VM_BLOCK/65536*8, 0, 0,
- &chan->vm_vram_pt[i]);
+ ret = nouveau_gpuobj_new(dev, NULL, NV50_VM_BLOCK / 0x10000 * 8,
+ 0, NVOBJ_FLAG_ZERO_ALLOC,
+ &chan->vm_vram_pt[i]);
if (ret) {
- NV_ERROR(dev, "Error creating VRAM page tables: %d\n",
- ret);
+ NV_ERROR(dev, "Error creating VRAM PGT: %d\n", ret);
dev_priv->vm_vram_pt_nr = i;
return ret;
}
- dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i]->gpuobj;
+ dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i];
- for (v = 0; v < dev_priv->vm_vram_pt[i]->im_pramin->size;
- v += 4)
- BAR0_WI32(dev_priv->vm_vram_pt[i], v, 0);
-
- BAR0_WI32(chan->vm_pd, 0x10 + (i*8),
- chan->vm_vram_pt[i]->instance | 0x61);
- BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0);
+ nv_wo32(chan->vm_pd, 0x10 + (i*8),
+ chan->vm_vram_pt[i]->vinst | 0x61);
+ nv_wo32(chan->vm_pd, 0x14 + (i*8), 0);
}
- /* DMA object for PRAMIN BAR */
- ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
- &priv->pramin_bar);
- if (ret)
- return ret;
- BAR0_WI32(priv->pramin_bar->gpuobj, 0x00, 0x7fc00000);
- BAR0_WI32(priv->pramin_bar->gpuobj, 0x04, dev_priv->ramin_size - 1);
- BAR0_WI32(priv->pramin_bar->gpuobj, 0x08, 0x00000000);
- BAR0_WI32(priv->pramin_bar->gpuobj, 0x0c, 0x00000000);
- BAR0_WI32(priv->pramin_bar->gpuobj, 0x10, 0x00000000);
- BAR0_WI32(priv->pramin_bar->gpuobj, 0x14, 0x00000000);
-
/* DMA object for FB BAR */
- ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
- &priv->fb_bar);
+ ret = nouveau_gpuobj_new(dev, chan, 6*4, 16, 0, &priv->fb_bar);
if (ret)
return ret;
- BAR0_WI32(priv->fb_bar->gpuobj, 0x00, 0x7fc00000);
- BAR0_WI32(priv->fb_bar->gpuobj, 0x04, 0x40000000 +
- pci_resource_len(dev->pdev, 1) - 1);
- BAR0_WI32(priv->fb_bar->gpuobj, 0x08, 0x40000000);
- BAR0_WI32(priv->fb_bar->gpuobj, 0x0c, 0x00000000);
- BAR0_WI32(priv->fb_bar->gpuobj, 0x10, 0x00000000);
- BAR0_WI32(priv->fb_bar->gpuobj, 0x14, 0x00000000);
+ nv_wo32(priv->fb_bar, 0x00, 0x7fc00000);
+ nv_wo32(priv->fb_bar, 0x04, 0x40000000 +
+ pci_resource_len(dev->pdev, 1) - 1);
+ nv_wo32(priv->fb_bar, 0x08, 0x40000000);
+ nv_wo32(priv->fb_bar, 0x0c, 0x00000000);
+ nv_wo32(priv->fb_bar, 0x10, 0x00000000);
+ nv_wo32(priv->fb_bar, 0x14, 0x00000000);
- /* Poke the relevant regs, and pray it works :) */
- nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
- nv_wr32(dev, NV50_PUNK_UNK1710, 0);
- nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
- NV50_PUNK_BAR_CFG_BASE_VALID);
- nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
- NV50_PUNK_BAR1_CTXDMA_VALID);
- nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
- NV50_PUNK_BAR3_CTXDMA_VALID);
+ dev_priv->engine.instmem.flush(dev);
+ nv_wr32(dev, 0x001708, 0x80000000 | (priv->fb_bar->cinst >> 4));
for (i = 0; i < 8; i++)
nv_wr32(dev, 0x1900 + (i*4), 0);
- /* Assume that praying isn't enough, check that we can re-read the
- * entire fake channel back from the PRAMIN BAR */
- for (i = 0; i < c_size; i += 4) {
- if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) {
- NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n",
- i);
- return -EINVAL;
- }
- }
-
- nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700);
-
- /* Global PRAMIN heap */
- if (drm_mm_init(&dev_priv->ramin_heap, c_size, dev_priv->ramin_size - c_size)) {
- NV_ERROR(dev, "Failed to init RAMIN heap\n");
- }
-
- /*XXX: incorrect, but needed to make hash func "work" */
- dev_priv->ramht_offset = 0x10000;
- dev_priv->ramht_bits = 9;
- dev_priv->ramht_size = (1 << dev_priv->ramht_bits) * 8;
return 0;
}
@@ -297,29 +248,24 @@ nv50_instmem_takedown(struct drm_device *dev)
if (!priv)
return;
+ dev_priv->ramin_available = false;
+
/* Restore state from before init */
for (i = 0x1700; i <= 0x1710; i += 4)
nv_wr32(dev, i, priv->save1700[(i - 0x1700) / 4]);
- nouveau_gpuobj_ref_del(dev, &priv->fb_bar);
- nouveau_gpuobj_ref_del(dev, &priv->pramin_bar);
- nouveau_gpuobj_ref_del(dev, &priv->pramin_pt);
+ nouveau_gpuobj_ref(NULL, &priv->fb_bar);
+ nouveau_gpuobj_ref(NULL, &priv->pramin_bar);
+ nouveau_gpuobj_ref(NULL, &priv->pramin_pt);
/* Destroy dummy channel */
if (chan) {
- for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
- nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
- dev_priv->vm_vram_pt[i] = NULL;
- }
+ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++)
+ nouveau_gpuobj_ref(NULL, &chan->vm_vram_pt[i]);
dev_priv->vm_vram_pt_nr = 0;
- nouveau_gpuobj_del(dev, &chan->vm_pd);
- nouveau_gpuobj_ref_del(dev, &chan->ramfc);
- nouveau_gpuobj_ref_del(dev, &chan->ramin);
- drm_mm_takedown(&chan->ramin_heap);
-
- dev_priv->fifos[0] = dev_priv->fifos[127] = NULL;
- kfree(chan);
+ nv50_channel_del(&dev_priv->fifos[0]);
+ dev_priv->fifos[127] = NULL;
}
dev_priv->engine.instmem.priv = NULL;
@@ -331,14 +277,14 @@ nv50_instmem_suspend(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->fifos[0];
- struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
+ struct nouveau_gpuobj *ramin = chan->ramin;
int i;
- ramin->im_backing_suspend = vmalloc(ramin->im_pramin->size);
+ ramin->im_backing_suspend = vmalloc(ramin->size);
if (!ramin->im_backing_suspend)
return -ENOMEM;
- for (i = 0; i < ramin->im_pramin->size; i += 4)
+ for (i = 0; i < ramin->size; i += 4)
ramin->im_backing_suspend[i/4] = nv_ri32(dev, i);
return 0;
}
@@ -349,23 +295,25 @@ nv50_instmem_resume(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
struct nouveau_channel *chan = dev_priv->fifos[0];
- struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
+ struct nouveau_gpuobj *ramin = chan->ramin;
int i;
- nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (ramin->im_backing_start >> 16));
- for (i = 0; i < ramin->im_pramin->size; i += 4)
- BAR0_WI32(ramin, i, ramin->im_backing_suspend[i/4]);
+ dev_priv->ramin_available = false;
+ dev_priv->ramin_base = ~0;
+ for (i = 0; i < ramin->size; i += 4)
+ nv_wo32(ramin, i, ramin->im_backing_suspend[i/4]);
+ dev_priv->ramin_available = true;
vfree(ramin->im_backing_suspend);
ramin->im_backing_suspend = NULL;
/* Poke the relevant regs, and pray it works :) */
- nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
+ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->vinst >> 12));
nv_wr32(dev, NV50_PUNK_UNK1710, 0);
- nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
+ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->vinst >> 12) |
NV50_PUNK_BAR_CFG_BASE_VALID);
- nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
+ nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->cinst >> 4) |
NV50_PUNK_BAR1_CTXDMA_VALID);
- nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
+ nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->cinst >> 4) |
NV50_PUNK_BAR3_CTXDMA_VALID);
for (i = 0; i < 8; i++)
@@ -381,7 +329,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
if (gpuobj->im_backing)
return -EINVAL;
- *sz = ALIGN(*sz, NV50_INSTMEM_PAGE_SIZE);
+ *sz = ALIGN(*sz, 4096);
if (*sz == 0)
return -EINVAL;
@@ -399,9 +347,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
return ret;
}
- gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
- gpuobj->im_backing_start <<= PAGE_SHIFT;
-
+ gpuobj->vinst = gpuobj->im_backing->bo.mem.start << PAGE_SHIFT;
return 0;
}
@@ -424,7 +370,7 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
- struct nouveau_gpuobj *pramin_pt = priv->pramin_pt->gpuobj;
+ struct nouveau_gpuobj *pramin_pt = priv->pramin_pt;
uint32_t pte, pte_end;
uint64_t vram;
@@ -436,11 +382,11 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
pte = (gpuobj->im_pramin->start >> 12) << 1;
pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
- vram = gpuobj->im_backing_start;
+ vram = gpuobj->vinst;
NV_DEBUG(dev, "pramin=0x%lx, pte=%d, pte_end=%d\n",
gpuobj->im_pramin->start, pte, pte_end);
- NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
+ NV_DEBUG(dev, "first vram page: 0x%010llx\n", gpuobj->vinst);
vram |= 1;
if (dev_priv->vram_sys_base) {
@@ -449,9 +395,10 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
}
while (pte < pte_end) {
- nv_wo32(dev, pramin_pt, pte++, lower_32_bits(vram));
- nv_wo32(dev, pramin_pt, pte++, upper_32_bits(vram));
- vram += NV50_INSTMEM_PAGE_SIZE;
+ nv_wo32(pramin_pt, (pte * 4) + 0, lower_32_bits(vram));
+ nv_wo32(pramin_pt, (pte * 4) + 4, upper_32_bits(vram));
+ vram += 0x1000;
+ pte += 2;
}
dev_priv->engine.instmem.flush(dev);
@@ -472,12 +419,17 @@ nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
if (gpuobj->im_bound == 0)
return -EINVAL;
+ /* can happen during late takedown */
+ if (unlikely(!dev_priv->ramin_available))
+ return 0;
+
pte = (gpuobj->im_pramin->start >> 12) << 1;
pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
while (pte < pte_end) {
- nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
- nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
+ nv_wo32(priv->pramin_pt, (pte * 4) + 0, 0x00000000);
+ nv_wo32(priv->pramin_pt, (pte * 4) + 4, 0x00000000);
+ pte += 2;
}
dev_priv->engine.instmem.flush(dev);
@@ -489,7 +441,7 @@ void
nv50_instmem_flush(struct drm_device *dev)
{
nv_wr32(dev, 0x00330c, 0x00000001);
- if (!nv_wait(0x00330c, 0x00000002, 0x00000000))
+ if (!nv_wait(dev, 0x00330c, 0x00000002, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
}
@@ -497,7 +449,7 @@ void
nv84_instmem_flush(struct drm_device *dev)
{
nv_wr32(dev, 0x070000, 0x00000001);
- if (!nv_wait(0x070000, 0x00000002, 0x00000000))
+ if (!nv_wait(dev, 0x070000, 0x00000002, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
}
@@ -505,7 +457,7 @@ void
nv50_vm_flush(struct drm_device *dev, int engine)
{
nv_wr32(dev, 0x100c80, (engine << 16) | 1);
- if (!nv_wait(0x100c80, 0x00000001, 0x00000000))
+ if (!nv_wait(dev, 0x100c80, 0x00000001, 0x00000000))
NV_ERROR(dev, "vm flush timeout: engine %d\n", engine);
}
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
new file mode 100644
index 000000000000..7dbb305d7e63
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_bios.h"
+#include "nouveau_pm.h"
+
+struct nv50_pm_state {
+ struct nouveau_pm_level *perflvl;
+ struct pll_lims pll;
+ enum pll_types type;
+ int N, M, P;
+};
+
+int
+nv50_pm_clock_get(struct drm_device *dev, u32 id)
+{
+ struct pll_lims pll;
+ int P, N, M, ret;
+ u32 reg0, reg1;
+
+ ret = get_pll_limits(dev, id, &pll);
+ if (ret)
+ return ret;
+
+ reg0 = nv_rd32(dev, pll.reg + 0);
+ reg1 = nv_rd32(dev, pll.reg + 4);
+ P = (reg0 & 0x00070000) >> 16;
+ N = (reg1 & 0x0000ff00) >> 8;
+ M = (reg1 & 0x000000ff);
+
+ return ((pll.refclk * N / M) >> P);
+}
+
+void *
+nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
+ u32 id, int khz)
+{
+ struct nv50_pm_state *state;
+ int dummy, ret;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+ state->type = id;
+ state->perflvl = perflvl;
+
+ ret = get_pll_limits(dev, id, &state->pll);
+ if (ret < 0) {
+ kfree(state);
+ return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
+ }
+
+ ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, &state->M,
+ &dummy, &dummy, &state->P);
+ if (ret < 0) {
+ kfree(state);
+ return ERR_PTR(ret);
+ }
+
+ return state;
+}
+
+void
+nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
+{
+ struct nv50_pm_state *state = pre_state;
+ struct nouveau_pm_level *perflvl = state->perflvl;
+ u32 reg = state->pll.reg, tmp;
+ struct bit_entry BIT_M;
+ u16 script;
+ int N = state->N;
+ int M = state->M;
+ int P = state->P;
+
+ if (state->type == PLL_MEMORY && perflvl->memscript &&
+ bit_table(dev, 'M', &BIT_M) == 0 &&
+ BIT_M.version == 1 && BIT_M.length >= 0x0b) {
+ script = ROM16(BIT_M.data[0x05]);
+ if (script)
+ nouveau_bios_run_init_table(dev, script, NULL);
+ script = ROM16(BIT_M.data[0x07]);
+ if (script)
+ nouveau_bios_run_init_table(dev, script, NULL);
+ script = ROM16(BIT_M.data[0x09]);
+ if (script)
+ nouveau_bios_run_init_table(dev, script, NULL);
+
+ nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
+ }
+
+ if (state->type == PLL_MEMORY) {
+ nv_wr32(dev, 0x100210, 0);
+ nv_wr32(dev, 0x1002dc, 1);
+ }
+
+ tmp = nv_rd32(dev, reg + 0) & 0xfff8ffff;
+ tmp |= 0x80000000 | (P << 16);
+ nv_wr32(dev, reg + 0, tmp);
+ nv_wr32(dev, reg + 4, (N << 8) | M);
+
+ if (state->type == PLL_MEMORY) {
+ nv_wr32(dev, 0x1002dc, 0);
+ nv_wr32(dev, 0x100210, 0x80000000);
+ }
+
+ kfree(state);
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index bcd4cf84a7e6..b4a5ecb199f9 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -92,7 +92,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
}
/* wait for it to be done */
- if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
+ if (!nv_wait(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or),
NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or);
NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or,
@@ -108,7 +108,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val |
NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING);
- if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or),
+ if (!nv_wait(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or),
NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or);
NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or,
diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c
new file mode 100644
index 000000000000..dbbafed36406
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nva3_pm.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_bios.h"
+#include "nouveau_pm.h"
+
+/*XXX: boards using limits 0x40 need fixing, the register layout
+ * is correct here, but, there's some other funny magic
+ * that modifies things, so it's not likely we'll set/read
+ * the correct timings yet.. working on it...
+ */
+
+struct nva3_pm_state {
+ struct pll_lims pll;
+ int N, M, P;
+};
+
+int
+nva3_pm_clock_get(struct drm_device *dev, u32 id)
+{
+ struct pll_lims pll;
+ int P, N, M, ret;
+ u32 reg;
+
+ ret = get_pll_limits(dev, id, &pll);
+ if (ret)
+ return ret;
+
+ reg = nv_rd32(dev, pll.reg + 4);
+ P = (reg & 0x003f0000) >> 16;
+ N = (reg & 0x0000ff00) >> 8;
+ M = (reg & 0x000000ff);
+ return pll.refclk * N / M / P;
+}
+
+void *
+nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
+ u32 id, int khz)
+{
+ struct nva3_pm_state *state;
+ int dummy, ret;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ ret = get_pll_limits(dev, id, &state->pll);
+ if (ret < 0) {
+ kfree(state);
+ return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
+ }
+
+ ret = nv50_calc_pll2(dev, &state->pll, khz, &state->N, &dummy,
+ &state->M, &state->P);
+ if (ret < 0) {
+ kfree(state);
+ return ERR_PTR(ret);
+ }
+
+ return state;
+}
+
+void
+nva3_pm_clock_set(struct drm_device *dev, void *pre_state)
+{
+ struct nva3_pm_state *state = pre_state;
+ u32 reg = state->pll.reg;
+
+ nv_wr32(dev, reg + 4, (state->P << 16) | (state->N << 8) | state->M);
+ kfree(state);
+}
+
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index d64375871979..890c2b95fbc1 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -43,12 +43,6 @@ nvc0_fifo_reassign(struct drm_device *dev, bool enable)
}
bool
-nvc0_fifo_cache_flush(struct drm_device *dev)
-{
- return true;
-}
-
-bool
nvc0_fifo_cache_pull(struct drm_device *dev, bool enable)
{
return false;
diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c
index 6b451f864783..13a0f78a9088 100644
--- a/drivers/gpu/drm/nouveau/nvc0_instmem.c
+++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c
@@ -50,8 +50,7 @@ nvc0_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
return ret;
}
- gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
- gpuobj->im_backing_start <<= PAGE_SHIFT;
+ gpuobj->vinst = gpuobj->im_backing->bo.mem.start << PAGE_SHIFT;
return 0;
}
@@ -84,11 +83,11 @@ nvc0_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
pte = gpuobj->im_pramin->start >> 12;
pte_end = (gpuobj->im_pramin->size >> 12) + pte;
- vram = gpuobj->im_backing_start;
+ vram = gpuobj->vinst;
NV_DEBUG(dev, "pramin=0x%lx, pte=%d, pte_end=%d\n",
gpuobj->im_pramin->start, pte, pte_end);
- NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
+ NV_DEBUG(dev, "first vram page: 0x%010llx\n", gpuobj->vinst);
while (pte < pte_end) {
nv_wr32(dev, 0x702000 + (pte * 8), (vram >> 8) | 1);
@@ -134,7 +133,7 @@ void
nvc0_instmem_flush(struct drm_device *dev)
{
nv_wr32(dev, 0x070000, 1);
- if (!nv_wait(0x070000, 0x00000002, 0x00000000))
+ if (!nv_wait(dev, 0x070000, 0x00000002, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
}
@@ -221,10 +220,6 @@ nvc0_instmem_init(struct drm_device *dev)
return -ENOMEM;
}
- /*XXX: incorrect, but needed to make hash func "work" */
- dev_priv->ramht_offset = 0x10000;
- dev_priv->ramht_bits = 9;
- dev_priv->ramht_size = (1 << dev_priv->ramht_bits) * 8;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
index ad64673ace1f..881f8a585613 100644
--- a/drivers/gpu/drm/nouveau/nvreg.h
+++ b/drivers/gpu/drm/nouveau/nvreg.h
@@ -263,6 +263,7 @@
# define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2
# define NV_CIO_CRE_LCD__INDEX 0x33
# define NV_CIO_CRE_LCD_LCD_SELECT 0:0
+# define NV_CIO_CRE_LCD_ROUTE_MASK 0x3b
# define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36
# define NV_CIO_CRE_DDC0_WR__INDEX 0x37
# define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */
diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
index d42c76c23714..18c3c71e41b1 100644
--- a/drivers/gpu/drm/r128/r128_drv.c
+++ b/drivers/gpu/drm/r128/r128_drv.c
@@ -56,8 +56,6 @@ static struct drm_driver driver = {
.irq_uninstall = r128_driver_irq_uninstall,
.irq_handler = r128_driver_irq_handler,
.reclaim_buffers = drm_core_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = r128_ioctls,
.dma_ioctl = r128_cce_buffers,
.fops = {
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index aebe00875041..6cae4f2028d2 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -65,7 +65,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \
- evergreen.o evergreen_cs.o
+ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index cd0290f946cf..df2b6f2b35f8 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -398,65 +398,76 @@ static void atombios_disable_ss(struct drm_crtc *crtc)
union atom_enable_ss {
- ENABLE_LVDS_SS_PARAMETERS legacy;
+ ENABLE_LVDS_SS_PARAMETERS lvds_ss;
+ ENABLE_LVDS_SS_PARAMETERS_V2 lvds_ss_2;
ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1;
+ ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 v2;
};
-static void atombios_enable_ss(struct drm_crtc *crtc)
+static void atombios_crtc_program_ss(struct drm_crtc *crtc,
+ int enable,
+ int pll_id,
+ struct radeon_atom_ss *ss)
{
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
- struct drm_encoder *encoder = NULL;
- struct radeon_encoder *radeon_encoder = NULL;
- struct radeon_encoder_atom_dig *dig = NULL;
int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
union atom_enable_ss args;
- uint16_t percentage = 0;
- uint8_t type = 0, step = 0, delay = 0, range = 0;
- /* XXX add ss support for DCE4 */
- if (ASIC_IS_DCE4(rdev))
- return;
+ memset(&args, 0, sizeof(args));
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- if (encoder->crtc == crtc) {
- radeon_encoder = to_radeon_encoder(encoder);
- /* only enable spread spectrum on LVDS */
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- dig = radeon_encoder->enc_priv;
- if (dig && dig->ss) {
- percentage = dig->ss->percentage;
- type = dig->ss->type;
- step = dig->ss->step;
- delay = dig->ss->delay;
- range = dig->ss->range;
- } else
- return;
- } else
- return;
+ if (ASIC_IS_DCE4(rdev)) {
+ args.v2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
+ args.v2.ucSpreadSpectrumType = ss->type;
+ switch (pll_id) {
+ case ATOM_PPLL1:
+ args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL;
+ args.v2.usSpreadSpectrumAmount = ss->amount;
+ args.v2.usSpreadSpectrumStep = ss->step;
+ break;
+ case ATOM_PPLL2:
+ args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P2PLL;
+ args.v2.usSpreadSpectrumAmount = ss->amount;
+ args.v2.usSpreadSpectrumStep = ss->step;
break;
+ case ATOM_DCPLL:
+ args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_DCPLL;
+ args.v2.usSpreadSpectrumAmount = 0;
+ args.v2.usSpreadSpectrumStep = 0;
+ break;
+ case ATOM_PPLL_INVALID:
+ return;
}
- }
-
- if (!radeon_encoder)
- return;
-
- memset(&args, 0, sizeof(args));
- if (ASIC_IS_AVIVO(rdev)) {
- args.v1.usSpreadSpectrumPercentage = cpu_to_le16(percentage);
- args.v1.ucSpreadSpectrumType = type;
- args.v1.ucSpreadSpectrumStep = step;
- args.v1.ucSpreadSpectrumDelay = delay;
- args.v1.ucSpreadSpectrumRange = range;
- args.v1.ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
- args.v1.ucEnable = ATOM_ENABLE;
+ args.v2.ucEnable = enable;
+ } else if (ASIC_IS_DCE3(rdev)) {
+ args.v1.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
+ args.v1.ucSpreadSpectrumType = ss->type;
+ args.v1.ucSpreadSpectrumStep = ss->step;
+ args.v1.ucSpreadSpectrumDelay = ss->delay;
+ args.v1.ucSpreadSpectrumRange = ss->range;
+ args.v1.ucPpll = pll_id;
+ args.v1.ucEnable = enable;
+ } else if (ASIC_IS_AVIVO(rdev)) {
+ if (enable == ATOM_DISABLE) {
+ atombios_disable_ss(crtc);
+ return;
+ }
+ args.lvds_ss_2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
+ args.lvds_ss_2.ucSpreadSpectrumType = ss->type;
+ args.lvds_ss_2.ucSpreadSpectrumStep = ss->step;
+ args.lvds_ss_2.ucSpreadSpectrumDelay = ss->delay;
+ args.lvds_ss_2.ucSpreadSpectrumRange = ss->range;
+ args.lvds_ss_2.ucEnable = enable;
} else {
- args.legacy.usSpreadSpectrumPercentage = cpu_to_le16(percentage);
- args.legacy.ucSpreadSpectrumType = type;
- args.legacy.ucSpreadSpectrumStepSize_Delay = (step & 3) << 2;
- args.legacy.ucSpreadSpectrumStepSize_Delay |= (delay & 7) << 4;
- args.legacy.ucEnable = ATOM_ENABLE;
+ if (enable == ATOM_DISABLE) {
+ atombios_disable_ss(crtc);
+ return;
+ }
+ args.lvds_ss.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
+ args.lvds_ss.ucSpreadSpectrumType = ss->type;
+ args.lvds_ss.ucSpreadSpectrumStepSize_Delay = (ss->step & 3) << 2;
+ args.lvds_ss.ucSpreadSpectrumStepSize_Delay |= (ss->delay & 7) << 4;
+ args.lvds_ss.ucEnable = enable;
}
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
}
@@ -468,7 +479,9 @@ union adjust_pixel_clock {
static u32 atombios_adjust_pll(struct drm_crtc *crtc,
struct drm_display_mode *mode,
- struct radeon_pll *pll)
+ struct radeon_pll *pll,
+ bool ss_enabled,
+ struct radeon_atom_ss *ss)
{
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -482,19 +495,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
/* reset the pll flags */
pll->flags = 0;
- /* select the PLL algo */
- if (ASIC_IS_AVIVO(rdev)) {
- if (radeon_new_pll == 0)
- pll->algo = PLL_ALGO_LEGACY;
- else
- pll->algo = PLL_ALGO_NEW;
- } else {
- if (radeon_new_pll == 1)
- pll->algo = PLL_ALGO_NEW;
- else
- pll->algo = PLL_ALGO_LEGACY;
- }
-
if (ASIC_IS_AVIVO(rdev)) {
if ((rdev->family == CHIP_RS600) ||
(rdev->family == CHIP_RS690) ||
@@ -531,29 +531,22 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
}
}
+ /* use recommended ref_div for ss */
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ if (ss_enabled) {
+ if (ss->refdiv) {
+ pll->flags |= RADEON_PLL_USE_REF_DIV;
+ pll->reference_div = ss->refdiv;
+ }
+ }
+ }
+
if (ASIC_IS_AVIVO(rdev)) {
/* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */
if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)
adjusted_clock = mode->clock * 2;
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
- pll->algo = PLL_ALGO_LEGACY;
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
pll->flags |= RADEON_PLL_PREFER_CLOSEST_LOWER;
- }
- /* There is some evidence (often anecdotal) that RV515/RV620 LVDS
- * (on some boards at least) prefers the legacy algo. I'm not
- * sure whether this should handled generically or on a
- * case-by-case quirk basis. Both algos should work fine in the
- * majority of cases.
- */
- if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) &&
- ((rdev->family == CHIP_RV515) ||
- (rdev->family == CHIP_RV620))) {
- /* allow the user to overrride just in case */
- if (radeon_new_pll == 1)
- pll->algo = PLL_ALGO_NEW;
- else
- pll->algo = PLL_ALGO_LEGACY;
- }
} else {
if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
@@ -589,9 +582,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
args.v1.ucTransmitterID = radeon_encoder->encoder_id;
args.v1.ucEncodeMode = encoder_mode;
if (encoder_mode == ATOM_ENCODER_MODE_DP) {
- /* may want to enable SS on DP eventually */
- /* args.v1.ucConfig |=
- ADJUST_DISPLAY_CONFIG_SS_ENABLE;*/
+ if (ss_enabled)
+ args.v1.ucConfig |=
+ ADJUST_DISPLAY_CONFIG_SS_ENABLE;
} else if (encoder_mode == ATOM_ENCODER_MODE_LVDS) {
args.v1.ucConfig |=
ADJUST_DISPLAY_CONFIG_SS_ENABLE;
@@ -608,11 +601,10 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
args.v3.sInput.ucDispPllConfig = 0;
if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
-
if (encoder_mode == ATOM_ENCODER_MODE_DP) {
- /* may want to enable SS on DP/eDP eventually */
- /*args.v3.sInput.ucDispPllConfig |=
- DISPPLL_CONFIG_SS_ENABLE;*/
+ if (ss_enabled)
+ args.v3.sInput.ucDispPllConfig |=
+ DISPPLL_CONFIG_SS_ENABLE;
args.v3.sInput.ucDispPllConfig |=
DISPPLL_CONFIG_COHERENT_MODE;
/* 16200 or 27000 */
@@ -632,17 +624,17 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
}
} else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
if (encoder_mode == ATOM_ENCODER_MODE_DP) {
- /* may want to enable SS on DP/eDP eventually */
- /*args.v3.sInput.ucDispPllConfig |=
- DISPPLL_CONFIG_SS_ENABLE;*/
+ if (ss_enabled)
+ args.v3.sInput.ucDispPllConfig |=
+ DISPPLL_CONFIG_SS_ENABLE;
args.v3.sInput.ucDispPllConfig |=
DISPPLL_CONFIG_COHERENT_MODE;
/* 16200 or 27000 */
args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
} else if (encoder_mode == ATOM_ENCODER_MODE_LVDS) {
- /* want to enable SS on LVDS eventually */
- /*args.v3.sInput.ucDispPllConfig |=
- DISPPLL_CONFIG_SS_ENABLE;*/
+ if (ss_enabled)
+ args.v3.sInput.ucDispPllConfig |=
+ DISPPLL_CONFIG_SS_ENABLE;
} else {
if (mode->clock > 165000)
args.v3.sInput.ucDispPllConfig |=
@@ -816,6 +808,8 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
struct radeon_pll *pll;
u32 adjusted_clock;
int encoder_mode = 0;
+ struct radeon_atom_ss ss;
+ bool ss_enabled = false;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc == crtc) {
@@ -842,25 +836,123 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
break;
}
+ if (radeon_encoder->active_device &
+ (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) {
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+ struct drm_connector *connector =
+ radeon_get_connector_for_encoder(encoder);
+ struct radeon_connector *radeon_connector =
+ to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector =
+ radeon_connector->con_priv;
+ int dp_clock;
+
+ switch (encoder_mode) {
+ case ATOM_ENCODER_MODE_DP:
+ /* DP/eDP */
+ dp_clock = dig_connector->dp_clock / 10;
+ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
+ if (ASIC_IS_DCE4(rdev))
+ ss_enabled =
+ radeon_atombios_get_asic_ss_info(rdev, &ss,
+ dig->lcd_ss_id,
+ dp_clock);
+ else
+ ss_enabled =
+ radeon_atombios_get_ppll_ss_info(rdev, &ss,
+ dig->lcd_ss_id);
+ } else {
+ if (ASIC_IS_DCE4(rdev))
+ ss_enabled =
+ radeon_atombios_get_asic_ss_info(rdev, &ss,
+ ASIC_INTERNAL_SS_ON_DP,
+ dp_clock);
+ else {
+ if (dp_clock == 16200) {
+ ss_enabled =
+ radeon_atombios_get_ppll_ss_info(rdev, &ss,
+ ATOM_DP_SS_ID2);
+ if (!ss_enabled)
+ ss_enabled =
+ radeon_atombios_get_ppll_ss_info(rdev, &ss,
+ ATOM_DP_SS_ID1);
+ } else
+ ss_enabled =
+ radeon_atombios_get_ppll_ss_info(rdev, &ss,
+ ATOM_DP_SS_ID1);
+ }
+ }
+ break;
+ case ATOM_ENCODER_MODE_LVDS:
+ if (ASIC_IS_DCE4(rdev))
+ ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss,
+ dig->lcd_ss_id,
+ mode->clock / 10);
+ else
+ ss_enabled = radeon_atombios_get_ppll_ss_info(rdev, &ss,
+ dig->lcd_ss_id);
+ break;
+ case ATOM_ENCODER_MODE_DVI:
+ if (ASIC_IS_DCE4(rdev))
+ ss_enabled =
+ radeon_atombios_get_asic_ss_info(rdev, &ss,
+ ASIC_INTERNAL_SS_ON_TMDS,
+ mode->clock / 10);
+ break;
+ case ATOM_ENCODER_MODE_HDMI:
+ if (ASIC_IS_DCE4(rdev))
+ ss_enabled =
+ radeon_atombios_get_asic_ss_info(rdev, &ss,
+ ASIC_INTERNAL_SS_ON_HDMI,
+ mode->clock / 10);
+ break;
+ default:
+ break;
+ }
+ }
+
/* adjust pixel clock as needed */
- adjusted_clock = atombios_adjust_pll(crtc, mode, pll);
+ adjusted_clock = atombios_adjust_pll(crtc, mode, pll, ss_enabled, &ss);
radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
&ref_div, &post_div);
+ atombios_crtc_program_ss(crtc, ATOM_DISABLE, radeon_crtc->pll_id, &ss);
+
atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
encoder_mode, radeon_encoder->encoder_id, mode->clock,
ref_div, fb_div, frac_fb_div, post_div);
+ if (ss_enabled) {
+ /* calculate ss amount and step size */
+ if (ASIC_IS_DCE4(rdev)) {
+ u32 step_size;
+ u32 amount = (((fb_div * 10) + frac_fb_div) * ss.percentage) / 10000;
+ ss.amount = (amount / 10) & ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK;
+ ss.amount |= ((amount - (ss.amount * 10)) << ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) &
+ ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK;
+ if (ss.type & ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD)
+ step_size = (4 * amount * ref_div * (ss.rate * 2048)) /
+ (125 * 25 * pll->reference_freq / 100);
+ else
+ step_size = (2 * amount * ref_div * (ss.rate * 2048)) /
+ (125 * 25 * pll->reference_freq / 100);
+ ss.step = step_size;
+ }
+
+ atombios_crtc_program_ss(crtc, ATOM_ENABLE, radeon_crtc->pll_id, &ss);
+ }
}
-static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
- struct drm_framebuffer *old_fb)
+static int evergreen_crtc_do_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, int atomic)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_framebuffer *radeon_fb;
+ struct drm_framebuffer *target_fb;
struct drm_gem_object *obj;
struct radeon_bo *rbo;
uint64_t fb_location;
@@ -868,28 +960,43 @@ static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
int r;
/* no fb bound */
- if (!crtc->fb) {
+ if (!atomic && !crtc->fb) {
DRM_DEBUG_KMS("No FB bound\n");
return 0;
}
- radeon_fb = to_radeon_framebuffer(crtc->fb);
+ if (atomic) {
+ radeon_fb = to_radeon_framebuffer(fb);
+ target_fb = fb;
+ }
+ else {
+ radeon_fb = to_radeon_framebuffer(crtc->fb);
+ target_fb = crtc->fb;
+ }
- /* Pin framebuffer & get tilling informations */
+ /* If atomic, assume fb object is pinned & idle & fenced and
+ * just update base pointers
+ */
obj = radeon_fb->obj;
rbo = obj->driver_private;
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
return r;
- r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
- if (unlikely(r != 0)) {
- radeon_bo_unreserve(rbo);
- return -EINVAL;
+
+ if (atomic)
+ fb_location = radeon_bo_gpu_offset(rbo);
+ else {
+ r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
+ if (unlikely(r != 0)) {
+ radeon_bo_unreserve(rbo);
+ return -EINVAL;
+ }
}
+
radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
radeon_bo_unreserve(rbo);
- switch (crtc->fb->bits_per_pixel) {
+ switch (target_fb->bits_per_pixel) {
case 8:
fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP) |
EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED));
@@ -909,7 +1016,7 @@ static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
break;
default:
DRM_ERROR("Unsupported screen depth %d\n",
- crtc->fb->bits_per_pixel);
+ target_fb->bits_per_pixel);
return -EINVAL;
}
@@ -955,10 +1062,10 @@ static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0);
WREG32(EVERGREEN_GRPH_X_START + radeon_crtc->crtc_offset, 0);
WREG32(EVERGREEN_GRPH_Y_START + radeon_crtc->crtc_offset, 0);
- WREG32(EVERGREEN_GRPH_X_END + radeon_crtc->crtc_offset, crtc->fb->width);
- WREG32(EVERGREEN_GRPH_Y_END + radeon_crtc->crtc_offset, crtc->fb->height);
+ WREG32(EVERGREEN_GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width);
+ WREG32(EVERGREEN_GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height);
- fb_pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8);
+ fb_pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8);
WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels);
WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1);
@@ -977,8 +1084,8 @@ static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
else
WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
- if (old_fb && old_fb != crtc->fb) {
- radeon_fb = to_radeon_framebuffer(old_fb);
+ if (!atomic && fb && fb != crtc->fb) {
+ radeon_fb = to_radeon_framebuffer(fb);
rbo = radeon_fb->obj->driver_private;
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
@@ -993,8 +1100,9 @@ static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
}
-static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
- struct drm_framebuffer *old_fb)
+static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, int atomic)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
@@ -1002,33 +1110,48 @@ static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
struct radeon_framebuffer *radeon_fb;
struct drm_gem_object *obj;
struct radeon_bo *rbo;
+ struct drm_framebuffer *target_fb;
uint64_t fb_location;
uint32_t fb_format, fb_pitch_pixels, tiling_flags;
int r;
/* no fb bound */
- if (!crtc->fb) {
+ if (!atomic && !crtc->fb) {
DRM_DEBUG_KMS("No FB bound\n");
return 0;
}
- radeon_fb = to_radeon_framebuffer(crtc->fb);
+ if (atomic) {
+ radeon_fb = to_radeon_framebuffer(fb);
+ target_fb = fb;
+ }
+ else {
+ radeon_fb = to_radeon_framebuffer(crtc->fb);
+ target_fb = crtc->fb;
+ }
- /* Pin framebuffer & get tilling informations */
obj = radeon_fb->obj;
rbo = obj->driver_private;
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
return r;
- r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
- if (unlikely(r != 0)) {
- radeon_bo_unreserve(rbo);
- return -EINVAL;
+
+ /* If atomic, assume fb object is pinned & idle & fenced and
+ * just update base pointers
+ */
+ if (atomic)
+ fb_location = radeon_bo_gpu_offset(rbo);
+ else {
+ r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
+ if (unlikely(r != 0)) {
+ radeon_bo_unreserve(rbo);
+ return -EINVAL;
+ }
}
radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
radeon_bo_unreserve(rbo);
- switch (crtc->fb->bits_per_pixel) {
+ switch (target_fb->bits_per_pixel) {
case 8:
fb_format =
AVIVO_D1GRPH_CONTROL_DEPTH_8BPP |
@@ -1052,7 +1175,7 @@ static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
break;
default:
DRM_ERROR("Unsupported screen depth %d\n",
- crtc->fb->bits_per_pixel);
+ target_fb->bits_per_pixel);
return -EINVAL;
}
@@ -1093,10 +1216,10 @@ static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0);
WREG32(AVIVO_D1GRPH_X_START + radeon_crtc->crtc_offset, 0);
WREG32(AVIVO_D1GRPH_Y_START + radeon_crtc->crtc_offset, 0);
- WREG32(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, crtc->fb->width);
- WREG32(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, crtc->fb->height);
+ WREG32(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width);
+ WREG32(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height);
- fb_pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8);
+ fb_pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8);
WREG32(AVIVO_D1GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels);
WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1);
@@ -1115,8 +1238,8 @@ static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
else
WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
- if (old_fb && old_fb != crtc->fb) {
- radeon_fb = to_radeon_framebuffer(old_fb);
+ if (!atomic && fb && fb != crtc->fb) {
+ radeon_fb = to_radeon_framebuffer(fb);
rbo = radeon_fb->obj->driver_private;
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
@@ -1138,11 +1261,26 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
struct radeon_device *rdev = dev->dev_private;
if (ASIC_IS_DCE4(rdev))
- return evergreen_crtc_set_base(crtc, x, y, old_fb);
+ return evergreen_crtc_do_set_base(crtc, old_fb, x, y, 0);
else if (ASIC_IS_AVIVO(rdev))
- return avivo_crtc_set_base(crtc, x, y, old_fb);
+ return avivo_crtc_do_set_base(crtc, old_fb, x, y, 0);
else
- return radeon_crtc_set_base(crtc, x, y, old_fb);
+ return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0);
+}
+
+int atombios_crtc_set_base_atomic(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, enum mode_set_atomic state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+
+ if (ASIC_IS_DCE4(rdev))
+ return evergreen_crtc_do_set_base(crtc, fb, x, y, 1);
+ else if (ASIC_IS_AVIVO(rdev))
+ return avivo_crtc_do_set_base(crtc, fb, x, y, 1);
+ else
+ return radeon_crtc_do_set_base(crtc, fb, x, y, 1);
}
/* properly set additional regs when using atombios */
@@ -1230,12 +1368,19 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
}
}
- atombios_disable_ss(crtc);
/* always set DCPLL */
- if (ASIC_IS_DCE4(rdev))
+ 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,
+ rdev->clock.default_dispclk);
+ if (ss_enabled)
+ atombios_crtc_program_ss(crtc, ATOM_DISABLE, ATOM_DCPLL, &ss);
atombios_crtc_set_dcpll(crtc);
+ if (ss_enabled)
+ atombios_crtc_program_ss(crtc, ATOM_ENABLE, ATOM_DCPLL, &ss);
+ }
atombios_crtc_set_pll(crtc, adjusted_mode);
- atombios_enable_ss(crtc);
if (ASIC_IS_DCE4(rdev))
atombios_set_crtc_dtd_timing(crtc, adjusted_mode);
@@ -1311,6 +1456,7 @@ static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
.mode_fixup = atombios_crtc_mode_fixup,
.mode_set = atombios_crtc_mode_set,
.mode_set_base = atombios_crtc_set_base,
+ .mode_set_base_atomic = atombios_crtc_set_base_atomic,
.prepare = atombios_crtc_prepare,
.commit = atombios_crtc_commit,
.load_lut = radeon_crtc_load_lut,
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 2f93d46ae69a..f12a5b3ec050 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -32,6 +32,7 @@
#include "atom.h"
#include "avivod.h"
#include "evergreen_reg.h"
+#include "evergreen_blit_shaders.h"
#define EVERGREEN_PFP_UCODE_SIZE 1120
#define EVERGREEN_PM4_UCODE_SIZE 1376
@@ -284,9 +285,444 @@ void evergreen_hpd_fini(struct radeon_device *rdev)
}
}
+/* watermark setup */
+
+static u32 evergreen_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 = 0;
+ /*
+ * 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 2:0:
+ * first display controller
+ * 0 - first half of lb (3840 * 2)
+ * 1 - first 3/4 of lb (5760 * 2)
+ * 2 - whole lb (7680 * 2)
+ * 3 - first 1/4 of lb (1920 * 2)
+ * second display controller
+ * 4 - second half of lb (3840 * 2)
+ * 5 - second 3/4 of lb (5760 * 2)
+ * 6 - whole lb (7680 * 2)
+ * 7 - last 1/4 of lb (1920 * 2)
+ */
+ if (mode && other_mode) {
+ if (mode->hdisplay > other_mode->hdisplay) {
+ if (mode->hdisplay > 2560)
+ tmp = 1; /* 3/4 */
+ else
+ tmp = 0; /* 1/2 */
+ } else if (other_mode->hdisplay > mode->hdisplay) {
+ if (other_mode->hdisplay > 2560)
+ tmp = 3; /* 1/4 */
+ else
+ tmp = 0; /* 1/2 */
+ } else
+ tmp = 0; /* 1/2 */
+ } else if (mode)
+ tmp = 2; /* whole */
+ else if (other_mode)
+ tmp = 3; /* 1/4 */
+
+ /* second controller of the pair uses second half of the lb */
+ if (radeon_crtc->crtc_id % 2)
+ tmp += 4;
+ WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, tmp);
+
+ switch (tmp) {
+ case 0:
+ case 4:
+ default:
+ return 3840 * 2;
+ case 1:
+ case 5:
+ return 5760 * 2;
+ case 2:
+ case 6:
+ return 7680 * 2;
+ case 3:
+ case 7:
+ return 1920 * 2;
+ }
+}
+
+static u32 evergreen_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;
+ }
+}
+
+struct evergreen_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 evergreen_dram_bandwidth(struct evergreen_wm_params *wm)
+{
+ /* Calculate DRAM Bandwidth and the part allocated to display. */
+ 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 evergreen_dram_bandwidth_for_display(struct evergreen_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 evergreen_data_return_bandwidth(struct evergreen_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 evergreen_dmif_request_bandwidth(struct evergreen_wm_params *wm)
+{
+ /* Calculate the DMIF Request Bandwidth */
+ fixed20_12 disp_clk_request_efficiency; /* 0.8 */
+ fixed20_12 disp_clk, bandwidth;
+ fixed20_12 a;
+
+ 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(10);
+ disp_clk_request_efficiency.full = dfixed_const(8);
+ disp_clk_request_efficiency.full = dfixed_div(disp_clk_request_efficiency, a);
+ a.full = dfixed_const(32);
+ bandwidth.full = dfixed_mul(a, disp_clk);
+ bandwidth.full = dfixed_mul(bandwidth, disp_clk_request_efficiency);
+
+ return dfixed_trunc(bandwidth);
+}
+
+static u32 evergreen_available_bandwidth(struct evergreen_wm_params *wm)
+{
+ /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */
+ u32 dram_bandwidth = evergreen_dram_bandwidth(wm);
+ u32 data_return_bandwidth = evergreen_data_return_bandwidth(wm);
+ u32 dmif_req_bandwidth = evergreen_dmif_request_bandwidth(wm);
+
+ return min(dram_bandwidth, min(data_return_bandwidth, dmif_req_bandwidth));
+}
+
+static u32 evergreen_average_bandwidth(struct evergreen_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 evergreen_latency_watermark(struct evergreen_wm_params *wm)
+{
+ /* First calcualte the latency in ns */
+ u32 mc_latency = 2000; /* 2000 ns. */
+ u32 available_bandwidth = evergreen_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;
+ 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(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(dfixed_trunc(a), 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 evergreen_average_bandwidth_vs_dram_bandwidth_for_display(struct evergreen_wm_params *wm)
+{
+ if (evergreen_average_bandwidth(wm) <=
+ (evergreen_dram_bandwidth_for_display(wm) / wm->num_heads))
+ return true;
+ else
+ return false;
+};
+
+static bool evergreen_average_bandwidth_vs_available_bandwidth(struct evergreen_wm_params *wm)
+{
+ if (evergreen_average_bandwidth(wm) <=
+ (evergreen_available_bandwidth(wm) / wm->num_heads))
+ return true;
+ else
+ return false;
+};
+
+static bool evergreen_check_latency_hiding(struct evergreen_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 (evergreen_latency_watermark(wm) <= latency_hiding)
+ return true;
+ else
+ return false;
+}
+
+static void evergreen_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 evergreen_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 pipe_offset = radeon_crtc->crtc_id * 16;
+ 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;
+ wm.dram_channels = evergreen_get_number_of_dram_channels(rdev);
+ wm.num_heads = num_heads;
+
+ /* set for high clocks */
+ latency_watermark_a = min(evergreen_latency_watermark(&wm), (u32)65535);
+ /* set for low clocks */
+ /* wm.yclk = low clk; wm.sclk = low clk */
+ latency_watermark_b = min(evergreen_latency_watermark(&wm), (u32)65535);
+
+ /* possibly force display priority to high */
+ /* should really do this at mode validation time... */
+ if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm) ||
+ !evergreen_average_bandwidth_vs_available_bandwidth(&wm) ||
+ !evergreen_check_latency_hiding(&wm) ||
+ (rdev->disp_priority == 2)) {
+ DRM_INFO("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(PIPE0_ARBITRATION_CONTROL3 + pipe_offset);
+ tmp = arb_control3;
+ tmp &= ~LATENCY_WATERMARK_MASK(3);
+ tmp |= LATENCY_WATERMARK_MASK(1);
+ WREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset, tmp);
+ WREG32(PIPE0_LATENCY_CONTROL + pipe_offset,
+ (LATENCY_LOW_WATERMARK(latency_watermark_a) |
+ LATENCY_HIGH_WATERMARK(line_time)));
+ /* select wm B */
+ tmp = RREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset);
+ tmp &= ~LATENCY_WATERMARK_MASK(3);
+ tmp |= LATENCY_WATERMARK_MASK(2);
+ WREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_offset, tmp);
+ WREG32(PIPE0_LATENCY_CONTROL + pipe_offset,
+ (LATENCY_LOW_WATERMARK(latency_watermark_b) |
+ LATENCY_HIGH_WATERMARK(line_time)));
+ /* restore original selection */
+ WREG32(PIPE0_ARBITRATION_CONTROL3 + pipe_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 evergreen_bandwidth_update(struct radeon_device *rdev)
{
- /* XXX */
+ 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 = evergreen_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i], mode0, mode1);
+ evergreen_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads);
+ lb_size = evergreen_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i+1], mode1, mode0);
+ evergreen_program_watermarks(rdev, rdev->mode_info.crtcs[i+1], lb_size, num_heads);
+ }
}
static int evergreen_mc_wait_for_idle(struct radeon_device *rdev)
@@ -677,7 +1113,7 @@ static int evergreen_cp_load_microcode(struct radeon_device *rdev)
static int evergreen_cp_start(struct radeon_device *rdev)
{
- int r;
+ int r, i;
uint32_t cp_me;
r = radeon_ring_lock(rdev, 7);
@@ -697,16 +1133,39 @@ static int evergreen_cp_start(struct radeon_device *rdev)
cp_me = 0xff;
WREG32(CP_ME_CNTL, cp_me);
- r = radeon_ring_lock(rdev, 4);
+ r = radeon_ring_lock(rdev, evergreen_default_size + 15);
if (r) {
DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r);
return r;
}
- /* init some VGT regs */
- radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONTEXT_REG, 2));
- radeon_ring_write(rdev, (VGT_VERTEX_REUSE_BLOCK_CNTL - PACKET3_SET_CONTEXT_REG_START) >> 2);
- radeon_ring_write(rdev, 0xe);
- radeon_ring_write(rdev, 0x10);
+
+ /* setup clear context state */
+ radeon_ring_write(rdev, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+ radeon_ring_write(rdev, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE);
+
+ for (i = 0; i < evergreen_default_size; i++)
+ radeon_ring_write(rdev, evergreen_default_state[i]);
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+ radeon_ring_write(rdev, PACKET3_PREAMBLE_END_CLEAR_STATE);
+
+ /* set clear context state */
+ radeon_ring_write(rdev, PACKET3(PACKET3_CLEAR_STATE, 0));
+ radeon_ring_write(rdev, 0);
+
+ /* SQ_VTX_BASE_VTX_LOC */
+ radeon_ring_write(rdev, 0xc0026f00);
+ radeon_ring_write(rdev, 0x00000000);
+ radeon_ring_write(rdev, 0x00000000);
+ radeon_ring_write(rdev, 0x00000000);
+
+ /* Clear consts */
+ radeon_ring_write(rdev, 0xc0036f00);
+ radeon_ring_write(rdev, 0x00000bc4);
+ radeon_ring_write(rdev, 0xffffffff);
+ radeon_ring_write(rdev, 0xffffffff);
+ radeon_ring_write(rdev, 0xffffffff);
+
radeon_ring_unlock_commit(rdev);
return 0;
@@ -731,7 +1190,7 @@ int evergreen_cp_resume(struct radeon_device *rdev)
/* Set ring buffer size */
rb_bufsz = drm_order(rdev->cp.ring_size / 8);
- tmp = RB_NO_UPDATE | (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+ tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
#ifdef __BIG_ENDIAN
tmp |= BUF_SWAP_32BIT;
#endif
@@ -745,8 +1204,19 @@ int evergreen_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB_CNTL, tmp | RB_RPTR_WR_ENA);
WREG32(CP_RB_RPTR_WR, 0);
WREG32(CP_RB_WPTR, 0);
- WREG32(CP_RB_RPTR_ADDR, rdev->cp.gpu_addr & 0xFFFFFFFF);
- WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->cp.gpu_addr));
+
+ /* set the wb address wether it's enabled or not */
+ WREG32(CP_RB_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC);
+ WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
+ WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
+
+ if (rdev->wb.enabled)
+ WREG32(SCRATCH_UMSK, 0xff);
+ else {
+ tmp |= RB_NO_UPDATE;
+ WREG32(SCRATCH_UMSK, 0);
+ }
+
mdelay(1);
WREG32(CP_RB_CNTL, tmp);
@@ -1584,6 +2054,7 @@ int evergreen_irq_set(struct radeon_device *rdev)
if (rdev->irq.sw_int) {
DRM_DEBUG("evergreen_irq_set: sw int\n");
cp_int_cntl |= RB_INT_ENABLE;
+ cp_int_cntl |= TIME_STAMP_INT_ENABLE;
}
if (rdev->irq.crtc_vblank_int[0]) {
DRM_DEBUG("evergreen_irq_set: vblank 0\n");
@@ -1760,8 +2231,10 @@ static inline u32 evergreen_get_ih_wptr(struct radeon_device *rdev)
{
u32 wptr, tmp;
- /* XXX use writeback */
- wptr = RREG32(IH_RB_WPTR);
+ if (rdev->wb.enabled)
+ wptr = 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
@@ -2000,6 +2473,7 @@ restart_ih:
break;
case 181: /* CP EOP event */
DRM_DEBUG("IH: CP EOP\n");
+ radeon_fence_process(rdev);
break;
case 233: /* GUI IDLE */
DRM_DEBUG("IH: CP EOP\n");
@@ -2048,26 +2522,18 @@ static int evergreen_startup(struct radeon_device *rdev)
return r;
}
evergreen_gpu_init(rdev);
-#if 0
- if (!rdev->r600_blit.shader_obj) {
- r = r600_blit_init(rdev);
- if (r) {
- DRM_ERROR("radeon: failed blitter (%d).\n", r);
- return r;
- }
- }
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (unlikely(r != 0))
- return r;
- r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
- &rdev->r600_blit.shader_gpu_addr);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+ r = evergreen_blit_init(rdev);
if (r) {
- DRM_ERROR("failed to pin blit object %d\n", r);
- return r;
+ evergreen_blit_fini(rdev);
+ rdev->asic->copy = NULL;
+ dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
-#endif
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
/* Enable IRQ */
r = r600_irq_init(rdev);
@@ -2087,8 +2553,6 @@ static int evergreen_startup(struct radeon_device *rdev)
r = evergreen_cp_resume(rdev);
if (r)
return r;
- /* write back buffer are not vital so don't worry about failure */
- r600_wb_enable(rdev);
return 0;
}
@@ -2122,23 +2586,43 @@ int evergreen_resume(struct radeon_device *rdev)
int evergreen_suspend(struct radeon_device *rdev)
{
-#if 0
int r;
-#endif
+
/* FIXME: we should wait for ring to be empty */
r700_cp_stop(rdev);
rdev->cp.ready = false;
evergreen_irq_suspend(rdev);
- r600_wb_disable(rdev);
+ radeon_wb_disable(rdev);
evergreen_pcie_gart_disable(rdev);
-#if 0
+
/* unpin shaders bo */
r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
if (likely(r == 0)) {
radeon_bo_unpin(rdev->r600_blit.shader_obj);
radeon_bo_unreserve(rdev->r600_blit.shader_obj);
}
-#endif
+
+ return 0;
+}
+
+int evergreen_copy_blit(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_pages, struct radeon_fence *fence)
+{
+ int r;
+
+ mutex_lock(&rdev->r600_blit.mutex);
+ rdev->r600_blit.vb_ib = NULL;
+ r = evergreen_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE);
+ if (r) {
+ if (rdev->r600_blit.vb_ib)
+ radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
+ mutex_unlock(&rdev->r600_blit.mutex);
+ return r;
+ }
+ evergreen_kms_blit_copy(rdev, src_offset, dst_offset, num_pages * RADEON_GPU_PAGE_SIZE);
+ evergreen_blit_done_copy(rdev, fence);
+ mutex_unlock(&rdev->r600_blit.mutex);
return 0;
}
@@ -2246,8 +2730,8 @@ int evergreen_init(struct radeon_device *rdev)
if (r) {
dev_err(rdev->dev, "disabling GPU acceleration\n");
r700_cp_fini(rdev);
- r600_wb_fini(rdev);
r600_irq_fini(rdev);
+ radeon_wb_fini(rdev);
radeon_irq_kms_fini(rdev);
evergreen_pcie_gart_fini(rdev);
rdev->accel_working = false;
@@ -2269,10 +2753,10 @@ int evergreen_init(struct radeon_device *rdev)
void evergreen_fini(struct radeon_device *rdev)
{
- /*r600_blit_fini(rdev);*/
+ evergreen_blit_fini(rdev);
r700_cp_fini(rdev);
- r600_wb_fini(rdev);
r600_irq_fini(rdev);
+ radeon_wb_fini(rdev);
radeon_irq_kms_fini(rdev);
evergreen_pcie_gart_fini(rdev);
radeon_gem_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/evergreen_blit_kms.c b/drivers/gpu/drm/radeon/evergreen_blit_kms.c
new file mode 100644
index 000000000000..086b9b0416c4
--- /dev/null
+++ b/drivers/gpu/drm/radeon/evergreen_blit_kms.c
@@ -0,0 +1,772 @@
+/*
+ * 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 (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 "drmP.h"
+#include "drm.h"
+#include "radeon_drm.h"
+#include "radeon.h"
+
+#include "evergreend.h"
+#include "evergreen_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
+
+/* emits 17 */
+static void
+set_render_target(struct radeon_device *rdev, int format,
+ int w, int h, u64 gpu_addr)
+{
+ u32 cb_color_info;
+ int pitch, slice;
+
+ h = ALIGN(h, 8);
+ if (h < 8)
+ h = 8;
+
+ cb_color_info = ((format << 2) | (1 << 24));
+ pitch = (w / 8) - 1;
+ slice = ((w * h) / 64) - 1;
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONTEXT_REG, 15));
+ radeon_ring_write(rdev, (CB_COLOR0_BASE - PACKET3_SET_CONTEXT_REG_START) >> 2);
+ radeon_ring_write(rdev, gpu_addr >> 8);
+ radeon_ring_write(rdev, pitch);
+ radeon_ring_write(rdev, slice);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, cb_color_info);
+ radeon_ring_write(rdev, (1 << 4));
+ radeon_ring_write(rdev, (w - 1) | ((h - 1) << 16));
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+}
+
+/* emits 5dw */
+static void
+cp_set_surface_sync(struct radeon_device *rdev,
+ u32 sync_type, u32 size,
+ u64 mc_addr)
+{
+ u32 cp_coher_size;
+
+ if (size == 0xffffffff)
+ cp_coher_size = 0xffffffff;
+ else
+ cp_coher_size = ((size + 255) >> 8);
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_SURFACE_SYNC, 3));
+ radeon_ring_write(rdev, sync_type);
+ radeon_ring_write(rdev, cp_coher_size);
+ radeon_ring_write(rdev, mc_addr >> 8);
+ radeon_ring_write(rdev, 10); /* poll interval */
+}
+
+/* emits 11dw + 1 surface sync = 16dw */
+static void
+set_shaders(struct radeon_device *rdev)
+{
+ u64 gpu_addr;
+
+ /* VS */
+ gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.vs_offset;
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONTEXT_REG, 3));
+ radeon_ring_write(rdev, (SQ_PGM_START_VS - PACKET3_SET_CONTEXT_REG_START) >> 2);
+ radeon_ring_write(rdev, gpu_addr >> 8);
+ radeon_ring_write(rdev, 2);
+ radeon_ring_write(rdev, 0);
+
+ /* PS */
+ gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.ps_offset;
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONTEXT_REG, 4));
+ radeon_ring_write(rdev, (SQ_PGM_START_PS - PACKET3_SET_CONTEXT_REG_START) >> 2);
+ radeon_ring_write(rdev, gpu_addr >> 8);
+ radeon_ring_write(rdev, 1);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 2);
+
+ gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.vs_offset;
+ cp_set_surface_sync(rdev, PACKET3_SH_ACTION_ENA, 512, gpu_addr);
+}
+
+/* emits 10 + 1 sync (5) = 15 */
+static void
+set_vtx_resource(struct radeon_device *rdev, u64 gpu_addr)
+{
+ u32 sq_vtx_constant_word2, sq_vtx_constant_word3;
+
+ /* high addr, stride */
+ sq_vtx_constant_word2 = ((upper_32_bits(gpu_addr) & 0xff) | (16 << 8));
+ /* xyzw swizzles */
+ sq_vtx_constant_word3 = (0 << 3) | (1 << 6) | (2 << 9) | (3 << 12);
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 8));
+ radeon_ring_write(rdev, 0x580);
+ radeon_ring_write(rdev, gpu_addr & 0xffffffff);
+ radeon_ring_write(rdev, 48 - 1); /* size */
+ radeon_ring_write(rdev, sq_vtx_constant_word2);
+ radeon_ring_write(rdev, sq_vtx_constant_word3);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, SQ_TEX_VTX_VALID_BUFFER << 30);
+
+ if (rdev->family == CHIP_CEDAR)
+ cp_set_surface_sync(rdev,
+ PACKET3_TC_ACTION_ENA, 48, gpu_addr);
+ else
+ cp_set_surface_sync(rdev,
+ PACKET3_VC_ACTION_ENA, 48, gpu_addr);
+
+}
+
+/* emits 10 */
+static void
+set_tex_resource(struct radeon_device *rdev,
+ int format, int w, int h, int pitch,
+ u64 gpu_addr)
+{
+ u32 sq_tex_resource_word0, sq_tex_resource_word1;
+ u32 sq_tex_resource_word4, sq_tex_resource_word7;
+
+ if (h < 1)
+ h = 1;
+
+ sq_tex_resource_word0 = (1 << 0); /* 2D */
+ sq_tex_resource_word0 |= ((((pitch >> 3) - 1) << 6) |
+ ((w - 1) << 18));
+ sq_tex_resource_word1 = ((h - 1) << 0);
+ /* xyzw swizzles */
+ sq_tex_resource_word4 = (0 << 16) | (1 << 19) | (2 << 22) | (3 << 25);
+
+ sq_tex_resource_word7 = format | (SQ_TEX_VTX_VALID_TEXTURE << 30);
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_RESOURCE, 8));
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, sq_tex_resource_word0);
+ radeon_ring_write(rdev, sq_tex_resource_word1);
+ radeon_ring_write(rdev, gpu_addr >> 8);
+ radeon_ring_write(rdev, gpu_addr >> 8);
+ radeon_ring_write(rdev, sq_tex_resource_word4);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, sq_tex_resource_word7);
+}
+
+/* emits 12 */
+static void
+set_scissors(struct radeon_device *rdev, int x1, int y1,
+ int x2, int y2)
+{
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONTEXT_REG, 2));
+ radeon_ring_write(rdev, (PA_SC_SCREEN_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_START) >> 2);
+ radeon_ring_write(rdev, (x1 << 0) | (y1 << 16));
+ radeon_ring_write(rdev, (x2 << 0) | (y2 << 16));
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONTEXT_REG, 2));
+ radeon_ring_write(rdev, (PA_SC_GENERIC_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_START) >> 2);
+ radeon_ring_write(rdev, (x1 << 0) | (y1 << 16) | (1 << 31));
+ radeon_ring_write(rdev, (x2 << 0) | (y2 << 16));
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONTEXT_REG, 2));
+ radeon_ring_write(rdev, (PA_SC_WINDOW_SCISSOR_TL - PACKET3_SET_CONTEXT_REG_START) >> 2);
+ radeon_ring_write(rdev, (x1 << 0) | (y1 << 16) | (1 << 31));
+ radeon_ring_write(rdev, (x2 << 0) | (y2 << 16));
+}
+
+/* emits 10 */
+static void
+draw_auto(struct radeon_device *rdev)
+{
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(rdev, (VGT_PRIMITIVE_TYPE - PACKET3_SET_CONFIG_REG_START) >> 2);
+ radeon_ring_write(rdev, DI_PT_RECTLIST);
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_INDEX_TYPE, 0));
+ radeon_ring_write(rdev, DI_INDEX_SIZE_16_BIT);
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_NUM_INSTANCES, 0));
+ radeon_ring_write(rdev, 1);
+
+ radeon_ring_write(rdev, PACKET3(PACKET3_DRAW_INDEX_AUTO, 1));
+ radeon_ring_write(rdev, 3);
+ radeon_ring_write(rdev, DI_SRC_SEL_AUTO_INDEX);
+
+}
+
+/* emits 30 */
+static void
+set_default_state(struct radeon_device *rdev)
+{
+ u32 sq_config, sq_gpr_resource_mgmt_1, sq_gpr_resource_mgmt_2, sq_gpr_resource_mgmt_3;
+ u32 sq_thread_resource_mgmt, sq_thread_resource_mgmt_2;
+ u32 sq_stack_resource_mgmt_1, sq_stack_resource_mgmt_2, sq_stack_resource_mgmt_3;
+ int num_ps_gprs, num_vs_gprs, num_temp_gprs;
+ int num_gs_gprs, num_es_gprs, num_hs_gprs, num_ls_gprs;
+ int num_ps_threads, num_vs_threads, num_gs_threads, num_es_threads;
+ int num_hs_threads, num_ls_threads;
+ int num_ps_stack_entries, num_vs_stack_entries, num_gs_stack_entries, num_es_stack_entries;
+ int num_hs_stack_entries, num_ls_stack_entries;
+
+ switch (rdev->family) {
+ case CHIP_CEDAR:
+ default:
+ num_ps_gprs = 93;
+ num_vs_gprs = 46;
+ num_temp_gprs = 4;
+ num_gs_gprs = 31;
+ num_es_gprs = 31;
+ num_hs_gprs = 23;
+ num_ls_gprs = 23;
+ num_ps_threads = 96;
+ num_vs_threads = 16;
+ num_gs_threads = 16;
+ num_es_threads = 16;
+ num_hs_threads = 16;
+ num_ls_threads = 16;
+ num_ps_stack_entries = 42;
+ num_vs_stack_entries = 42;
+ num_gs_stack_entries = 42;
+ num_es_stack_entries = 42;
+ num_hs_stack_entries = 42;
+ num_ls_stack_entries = 42;
+ break;
+ case CHIP_REDWOOD:
+ num_ps_gprs = 93;
+ num_vs_gprs = 46;
+ num_temp_gprs = 4;
+ num_gs_gprs = 31;
+ num_es_gprs = 31;
+ num_hs_gprs = 23;
+ num_ls_gprs = 23;
+ num_ps_threads = 128;
+ num_vs_threads = 20;
+ num_gs_threads = 20;
+ num_es_threads = 20;
+ num_hs_threads = 20;
+ num_ls_threads = 20;
+ num_ps_stack_entries = 42;
+ num_vs_stack_entries = 42;
+ num_gs_stack_entries = 42;
+ num_es_stack_entries = 42;
+ num_hs_stack_entries = 42;
+ num_ls_stack_entries = 42;
+ break;
+ case CHIP_JUNIPER:
+ num_ps_gprs = 93;
+ num_vs_gprs = 46;
+ num_temp_gprs = 4;
+ num_gs_gprs = 31;
+ num_es_gprs = 31;
+ num_hs_gprs = 23;
+ num_ls_gprs = 23;
+ num_ps_threads = 128;
+ num_vs_threads = 20;
+ num_gs_threads = 20;
+ num_es_threads = 20;
+ num_hs_threads = 20;
+ num_ls_threads = 20;
+ num_ps_stack_entries = 85;
+ num_vs_stack_entries = 85;
+ num_gs_stack_entries = 85;
+ num_es_stack_entries = 85;
+ num_hs_stack_entries = 85;
+ num_ls_stack_entries = 85;
+ break;
+ case CHIP_CYPRESS:
+ case CHIP_HEMLOCK:
+ num_ps_gprs = 93;
+ num_vs_gprs = 46;
+ num_temp_gprs = 4;
+ num_gs_gprs = 31;
+ num_es_gprs = 31;
+ num_hs_gprs = 23;
+ num_ls_gprs = 23;
+ num_ps_threads = 128;
+ num_vs_threads = 20;
+ num_gs_threads = 20;
+ num_es_threads = 20;
+ num_hs_threads = 20;
+ num_ls_threads = 20;
+ num_ps_stack_entries = 85;
+ num_vs_stack_entries = 85;
+ num_gs_stack_entries = 85;
+ num_es_stack_entries = 85;
+ num_hs_stack_entries = 85;
+ num_ls_stack_entries = 85;
+ break;
+ }
+
+ if (rdev->family == CHIP_CEDAR)
+ sq_config = 0;
+ else
+ sq_config = VC_ENABLE;
+
+ sq_config |= (EXPORT_SRC_C |
+ CS_PRIO(0) |
+ LS_PRIO(0) |
+ HS_PRIO(0) |
+ PS_PRIO(0) |
+ VS_PRIO(1) |
+ GS_PRIO(2) |
+ ES_PRIO(3));
+
+ sq_gpr_resource_mgmt_1 = (NUM_PS_GPRS(num_ps_gprs) |
+ NUM_VS_GPRS(num_vs_gprs) |
+ NUM_CLAUSE_TEMP_GPRS(num_temp_gprs));
+ sq_gpr_resource_mgmt_2 = (NUM_GS_GPRS(num_gs_gprs) |
+ NUM_ES_GPRS(num_es_gprs));
+ sq_gpr_resource_mgmt_3 = (NUM_HS_GPRS(num_hs_gprs) |
+ NUM_LS_GPRS(num_ls_gprs));
+ sq_thread_resource_mgmt = (NUM_PS_THREADS(num_ps_threads) |
+ NUM_VS_THREADS(num_vs_threads) |
+ NUM_GS_THREADS(num_gs_threads) |
+ NUM_ES_THREADS(num_es_threads));
+ sq_thread_resource_mgmt_2 = (NUM_HS_THREADS(num_hs_threads) |
+ NUM_LS_THREADS(num_ls_threads));
+ sq_stack_resource_mgmt_1 = (NUM_PS_STACK_ENTRIES(num_ps_stack_entries) |
+ NUM_VS_STACK_ENTRIES(num_vs_stack_entries));
+ sq_stack_resource_mgmt_2 = (NUM_GS_STACK_ENTRIES(num_gs_stack_entries) |
+ NUM_ES_STACK_ENTRIES(num_es_stack_entries));
+ sq_stack_resource_mgmt_3 = (NUM_HS_STACK_ENTRIES(num_hs_stack_entries) |
+ NUM_LS_STACK_ENTRIES(num_ls_stack_entries));
+
+ /* set clear context state */
+ radeon_ring_write(rdev, PACKET3(PACKET3_CLEAR_STATE, 0));
+ radeon_ring_write(rdev, 0);
+
+ /* disable dyn gprs */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(rdev, (SQ_DYN_GPR_CNTL_PS_FLUSH_REQ - PACKET3_SET_CONFIG_REG_START) >> 2);
+ radeon_ring_write(rdev, 0);
+
+ /* SQ config */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 11));
+ radeon_ring_write(rdev, (SQ_CONFIG - PACKET3_SET_CONFIG_REG_START) >> 2);
+ radeon_ring_write(rdev, sq_config);
+ radeon_ring_write(rdev, sq_gpr_resource_mgmt_1);
+ radeon_ring_write(rdev, sq_gpr_resource_mgmt_2);
+ radeon_ring_write(rdev, sq_gpr_resource_mgmt_3);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, 0);
+ radeon_ring_write(rdev, sq_thread_resource_mgmt);
+ radeon_ring_write(rdev, sq_thread_resource_mgmt_2);
+ radeon_ring_write(rdev, sq_stack_resource_mgmt_1);
+ radeon_ring_write(rdev, sq_stack_resource_mgmt_2);
+ radeon_ring_write(rdev, sq_stack_resource_mgmt_3);
+
+ /* CONTEXT_CONTROL */
+ radeon_ring_write(rdev, 0xc0012800);
+ radeon_ring_write(rdev, 0x80000000);
+ radeon_ring_write(rdev, 0x80000000);
+
+ /* SQ_VTX_BASE_VTX_LOC */
+ radeon_ring_write(rdev, 0xc0026f00);
+ radeon_ring_write(rdev, 0x00000000);
+ radeon_ring_write(rdev, 0x00000000);
+ radeon_ring_write(rdev, 0x00000000);
+
+ /* SET_SAMPLER */
+ radeon_ring_write(rdev, 0xc0036e00);
+ radeon_ring_write(rdev, 0x00000000);
+ radeon_ring_write(rdev, 0x00000012);
+ radeon_ring_write(rdev, 0x00000000);
+ radeon_ring_write(rdev, 0x00000000);
+
+}
+
+static inline uint32_t i2f(uint32_t input)
+{
+ u32 result, i, exponent, fraction;
+
+ if ((input & 0x3fff) == 0)
+ result = 0; /* 0 is a special case */
+ else {
+ exponent = 140; /* exponent biased by 127; */
+ fraction = (input & 0x3fff) << 10; /* cheat and only
+ handle numbers below 2^^15 */
+ for (i = 0; i < 14; i++) {
+ if (fraction & 0x800000)
+ break;
+ else {
+ fraction = fraction << 1; /* keep
+ shifting left until top bit = 1 */
+ exponent = exponent - 1;
+ }
+ }
+ result = exponent << 23 | (fraction & 0x7fffff); /* mask
+ off top bit; assumed 1 */
+ }
+ return result;
+}
+
+int evergreen_blit_init(struct radeon_device *rdev)
+{
+ u32 obj_size;
+ int r;
+ void *ptr;
+
+ /* pin copy shader into vram if already initialized */
+ if (rdev->r600_blit.shader_obj)
+ goto done;
+
+ mutex_init(&rdev->r600_blit.mutex);
+ rdev->r600_blit.state_offset = 0;
+ rdev->r600_blit.state_len = 0;
+ obj_size = 0;
+
+ rdev->r600_blit.vs_offset = obj_size;
+ obj_size += evergreen_vs_size * 4;
+ obj_size = ALIGN(obj_size, 256);
+
+ rdev->r600_blit.ps_offset = obj_size;
+ obj_size += evergreen_ps_size * 4;
+ obj_size = ALIGN(obj_size, 256);
+
+ r = radeon_bo_create(rdev, NULL, obj_size, true, RADEON_GEM_DOMAIN_VRAM,
+ &rdev->r600_blit.shader_obj);
+ if (r) {
+ DRM_ERROR("evergreen failed to allocate shader\n");
+ return r;
+ }
+
+ DRM_DEBUG("evergreen blit allocated bo %08x vs %08x ps %08x\n",
+ obj_size,
+ rdev->r600_blit.vs_offset, rdev->r600_blit.ps_offset);
+
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+ if (unlikely(r != 0))
+ return r;
+ r = radeon_bo_kmap(rdev->r600_blit.shader_obj, &ptr);
+ if (r) {
+ DRM_ERROR("failed to map blit object %d\n", r);
+ return r;
+ }
+
+ memcpy(ptr + rdev->r600_blit.vs_offset, evergreen_vs, evergreen_vs_size * 4);
+ memcpy(ptr + rdev->r600_blit.ps_offset, evergreen_ps, evergreen_ps_size * 4);
+ radeon_bo_kunmap(rdev->r600_blit.shader_obj);
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+
+done:
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+ if (unlikely(r != 0))
+ return r;
+ r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
+ &rdev->r600_blit.shader_gpu_addr);
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+ if (r) {
+ dev_err(rdev->dev, "(%d) pin blit object failed\n", r);
+ return r;
+ }
+ return 0;
+}
+
+void evergreen_blit_fini(struct radeon_device *rdev)
+{
+ int r;
+
+ if (rdev->r600_blit.shader_obj == NULL)
+ return;
+ /* If we can't reserve the bo, unref should be enough to destroy
+ * it when it becomes idle.
+ */
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+ if (!r) {
+ radeon_bo_unpin(rdev->r600_blit.shader_obj);
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+ }
+ radeon_bo_unref(&rdev->r600_blit.shader_obj);
+}
+
+static int evergreen_vb_ib_get(struct radeon_device *rdev)
+{
+ int r;
+ r = radeon_ib_get(rdev, &rdev->r600_blit.vb_ib);
+ if (r) {
+ DRM_ERROR("failed to get IB for vertex buffer\n");
+ return r;
+ }
+
+ rdev->r600_blit.vb_total = 64*1024;
+ rdev->r600_blit.vb_used = 0;
+ return 0;
+}
+
+static void evergreen_vb_ib_put(struct radeon_device *rdev)
+{
+ radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence);
+ radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
+}
+
+int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
+{
+ int r;
+ int ring_size, line_size;
+ int max_size;
+ /* loops of emits + fence emit possible */
+ int dwords_per_loop = 74, num_loops;
+
+ r = evergreen_vb_ib_get(rdev);
+ if (r)
+ return r;
+
+ /* 8 bpp vs 32 bpp for xfer unit */
+ if (size_bytes & 3)
+ line_size = 8192;
+ else
+ line_size = 8192 * 4;
+
+ max_size = 8192 * line_size;
+
+ /* major loops cover the max size transfer */
+ num_loops = ((size_bytes + max_size) / max_size);
+ /* minor loops cover the extra non aligned bits */
+ num_loops += ((size_bytes % line_size) ? 1 : 0);
+ /* calculate number of loops correctly */
+ ring_size = num_loops * dwords_per_loop;
+ /* set default + shaders */
+ ring_size += 46; /* shaders + def state */
+ ring_size += 10; /* fence emit for VB IB */
+ ring_size += 5; /* done copy */
+ ring_size += 10; /* fence emit for done copy */
+ r = radeon_ring_lock(rdev, ring_size);
+ if (r)
+ return r;
+
+ set_default_state(rdev); /* 30 */
+ set_shaders(rdev); /* 16 */
+ return 0;
+}
+
+void evergreen_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence)
+{
+ int r;
+
+ if (rdev->r600_blit.vb_ib)
+ evergreen_vb_ib_put(rdev);
+
+ if (fence)
+ r = radeon_fence_emit(rdev, fence);
+
+ radeon_ring_unlock_commit(rdev);
+}
+
+void evergreen_kms_blit_copy(struct radeon_device *rdev,
+ u64 src_gpu_addr, u64 dst_gpu_addr,
+ int size_bytes)
+{
+ int max_bytes;
+ u64 vb_gpu_addr;
+ u32 *vb;
+
+ DRM_DEBUG("emitting copy %16llx %16llx %d %d\n", src_gpu_addr, dst_gpu_addr,
+ size_bytes, rdev->r600_blit.vb_used);
+ vb = (u32 *)(rdev->r600_blit.vb_ib->ptr + rdev->r600_blit.vb_used);
+ if ((size_bytes & 3) || (src_gpu_addr & 3) || (dst_gpu_addr & 3)) {
+ max_bytes = 8192;
+
+ while (size_bytes) {
+ int cur_size = size_bytes;
+ int src_x = src_gpu_addr & 255;
+ int dst_x = dst_gpu_addr & 255;
+ int h = 1;
+ src_gpu_addr = src_gpu_addr & ~255ULL;
+ dst_gpu_addr = dst_gpu_addr & ~255ULL;
+
+ if (!src_x && !dst_x) {
+ h = (cur_size / max_bytes);
+ if (h > 8192)
+ h = 8192;
+ if (h == 0)
+ h = 1;
+ else
+ cur_size = max_bytes;
+ } else {
+ if (cur_size > max_bytes)
+ cur_size = max_bytes;
+ if (cur_size > (max_bytes - dst_x))
+ cur_size = (max_bytes - dst_x);
+ if (cur_size > (max_bytes - src_x))
+ cur_size = (max_bytes - src_x);
+ }
+
+ if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
+ WARN_ON(1);
+ }
+
+ vb[0] = i2f(dst_x);
+ vb[1] = 0;
+ vb[2] = i2f(src_x);
+ vb[3] = 0;
+
+ vb[4] = i2f(dst_x);
+ vb[5] = i2f(h);
+ vb[6] = i2f(src_x);
+ vb[7] = i2f(h);
+
+ vb[8] = i2f(dst_x + cur_size);
+ vb[9] = i2f(h);
+ vb[10] = i2f(src_x + cur_size);
+ vb[11] = i2f(h);
+
+ /* src 10 */
+ set_tex_resource(rdev, FMT_8,
+ src_x + cur_size, h, src_x + cur_size,
+ src_gpu_addr);
+
+ /* 5 */
+ cp_set_surface_sync(rdev,
+ PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
+
+
+ /* dst 17 */
+ set_render_target(rdev, COLOR_8,
+ dst_x + cur_size, h,
+ dst_gpu_addr);
+
+ /* scissors 12 */
+ set_scissors(rdev, dst_x, 0, dst_x + cur_size, h);
+
+ /* 15 */
+ vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used;
+ set_vtx_resource(rdev, vb_gpu_addr);
+
+ /* draw 10 */
+ draw_auto(rdev);
+
+ /* 5 */
+ cp_set_surface_sync(rdev,
+ PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA,
+ cur_size * h, dst_gpu_addr);
+
+ vb += 12;
+ rdev->r600_blit.vb_used += 12 * 4;
+
+ src_gpu_addr += cur_size * h;
+ dst_gpu_addr += cur_size * h;
+ size_bytes -= cur_size * h;
+ }
+ } else {
+ max_bytes = 8192 * 4;
+
+ while (size_bytes) {
+ int cur_size = size_bytes;
+ int src_x = (src_gpu_addr & 255);
+ int dst_x = (dst_gpu_addr & 255);
+ int h = 1;
+ src_gpu_addr = src_gpu_addr & ~255ULL;
+ dst_gpu_addr = dst_gpu_addr & ~255ULL;
+
+ if (!src_x && !dst_x) {
+ h = (cur_size / max_bytes);
+ if (h > 8192)
+ h = 8192;
+ if (h == 0)
+ h = 1;
+ else
+ cur_size = max_bytes;
+ } else {
+ if (cur_size > max_bytes)
+ cur_size = max_bytes;
+ if (cur_size > (max_bytes - dst_x))
+ cur_size = (max_bytes - dst_x);
+ if (cur_size > (max_bytes - src_x))
+ cur_size = (max_bytes - src_x);
+ }
+
+ if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
+ WARN_ON(1);
+ }
+
+ vb[0] = i2f(dst_x / 4);
+ vb[1] = 0;
+ vb[2] = i2f(src_x / 4);
+ vb[3] = 0;
+
+ vb[4] = i2f(dst_x / 4);
+ vb[5] = i2f(h);
+ vb[6] = i2f(src_x / 4);
+ vb[7] = i2f(h);
+
+ vb[8] = i2f((dst_x + cur_size) / 4);
+ vb[9] = i2f(h);
+ vb[10] = i2f((src_x + cur_size) / 4);
+ vb[11] = i2f(h);
+
+ /* src 10 */
+ set_tex_resource(rdev, FMT_8_8_8_8,
+ (src_x + cur_size) / 4,
+ h, (src_x + cur_size) / 4,
+ src_gpu_addr);
+ /* 5 */
+ cp_set_surface_sync(rdev,
+ PACKET3_TC_ACTION_ENA, (src_x + cur_size * h), src_gpu_addr);
+
+ /* dst 17 */
+ set_render_target(rdev, COLOR_8_8_8_8,
+ (dst_x + cur_size) / 4, h,
+ dst_gpu_addr);
+
+ /* scissors 12 */
+ set_scissors(rdev, (dst_x / 4), 0, (dst_x + cur_size / 4), h);
+
+ /* Vertex buffer setup 15 */
+ vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used;
+ set_vtx_resource(rdev, vb_gpu_addr);
+
+ /* draw 10 */
+ draw_auto(rdev);
+
+ /* 5 */
+ cp_set_surface_sync(rdev,
+ PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA,
+ cur_size * h, dst_gpu_addr);
+
+ /* 74 ring dwords per loop */
+ vb += 12;
+ rdev->r600_blit.vb_used += 12 * 4;
+
+ src_gpu_addr += cur_size * h;
+ dst_gpu_addr += cur_size * h;
+ size_bytes -= cur_size * h;
+ }
+ }
+}
+
diff --git a/drivers/gpu/drm/radeon/evergreen_blit_shaders.c b/drivers/gpu/drm/radeon/evergreen_blit_shaders.c
new file mode 100644
index 000000000000..ef1d28c07fbf
--- /dev/null
+++ b/drivers/gpu/drm/radeon/evergreen_blit_shaders.c
@@ -0,0 +1,348 @@
+/*
+ * 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 (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/kernel.h>
+
+/*
+ * evergreen cards need to use the 3D engine to blit data which requires
+ * quite a bit of hw state setup. Rather than pull the whole 3D driver
+ * (which normally generates the 3D state) into the DRM, we opt to use
+ * statically generated state tables. The regsiter state and shaders
+ * were hand generated to support blitting functionality. See the 3D
+ * driver or documentation for descriptions of the registers and
+ * shader instructions.
+ */
+
+const u32 evergreen_default_state[] =
+{
+ 0xc0016900,
+ 0x0000023b,
+ 0x00000000, /* SQ_LDS_ALLOC_PS */
+
+ 0xc0066900,
+ 0x00000240,
+ 0x00000000, /* SQ_ESGS_RING_ITEMSIZE */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+
+ 0xc0046900,
+ 0x00000247,
+ 0x00000000, /* SQ_GS_VERT_ITEMSIZE */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+
+ 0xc0026900,
+ 0x00000010,
+ 0x00000000, /* DB_Z_INFO */
+ 0x00000000, /* DB_STENCIL_INFO */
+
+ 0xc0016900,
+ 0x00000200,
+ 0x00000000, /* DB_DEPTH_CONTROL */
+
+ 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 */
+
+ 0xc0026900,
+ 0x0000000a,
+ 0x00000000, /* DB_STENCIL_CLEAR */
+ 0x00000000, /* DB_DEPTH_CLEAR */
+
+ 0xc0016900,
+ 0x000002dc,
+ 0x0000aa00, /* DB_ALPHA_TO_MASK */
+
+ 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 */
+
+ 0xc0016900,
+ 0x000000d4,
+ 0x00000000, /* SX_MISC */
+
+ 0xc0026900,
+ 0x00000292,
+ 0x00000000, /* PA_SC_MODE_CNTL_0 */
+ 0x00000000, /* PA_SC_MODE_CNTL_1 */
+
+ 0xc0106900,
+ 0x00000300,
+ 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_0 */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* PA_SC_AA_SAMPLE_LOCS_7 */
+ 0xffffffff, /* PA_SC_AA_MASK */
+
+ 0xc00d6900,
+ 0x00000202,
+ 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, /* */
+ 0x00000000, /* SQ_DYN_GPR_RESOURCE_LIMIT_1 */
+
+ 0xc0066900,
+ 0x000002de,
+ 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+
+ 0xc0016900,
+ 0x00000229,
+ 0x00000000, /* SQ_PGM_START_FS */
+
+ 0xc0016900,
+ 0x0000022a,
+ 0x00000000, /* SQ_PGM_RESOURCES_FS */
+
+ 0xc0096900,
+ 0x00000100,
+ 0x00ffffff, /* VGT_MAX_VTX_INDX */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* SX_ALPHA_TEST_CONTROL */
+ 0x00000000, /* CB_BLEND_RED */
+ 0x00000000, /* CB_BLEND_GREEN */
+ 0x00000000, /* CB_BLEND_BLUE */
+ 0x00000000, /* CB_BLEND_ALPHA */
+
+ 0xc0026900,
+ 0x000002a8,
+ 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */
+ 0x00000000, /* */
+
+ 0xc0026900,
+ 0x000002ad,
+ 0x00000000, /* VGT_REUSE_OFF */
+ 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 */
+
+ 0xc0016900,
+ 0x000002a1,
+ 0x00000000, /* VGT_PRIMITIVEID_EN */
+
+ 0xc0016900,
+ 0x000002a5,
+ 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_EN */
+
+ 0xc0016900,
+ 0x000002d5,
+ 0x00000000, /* VGT_SHADER_STAGES_EN */
+
+ 0xc0026900,
+ 0x000002e5,
+ 0x00000000, /* VGT_STRMOUT_CONFIG */
+ 0x00000000, /* */
+
+ 0xc0016900,
+ 0x000001e0,
+ 0x00000000, /* CB_BLEND0_CONTROL */
+
+ 0xc0016900,
+ 0x000001b1,
+ 0x00000000, /* SPI_VS_OUT_CONFIG */
+
+ 0xc0016900,
+ 0x00000187,
+ 0x00000000, /* SPI_VS_OUT_ID_0 */
+
+ 0xc0016900,
+ 0x00000191,
+ 0x00000100, /* SPI_PS_INPUT_CNTL_0 */
+
+ 0xc00b6900,
+ 0x000001b3,
+ 0x20000001, /* SPI_PS_IN_CONTROL_0 */
+ 0x00000000, /* SPI_PS_IN_CONTROL_1 */
+ 0x00000000, /* SPI_INTERP_CONTROL_0 */
+ 0x00000000, /* SPI_INPUT_Z */
+ 0x00000000, /* SPI_FOG_CNTL */
+ 0x00100000, /* SPI_BARYC_CNTL */
+ 0x00000000, /* SPI_PS_IN_CONTROL_2 */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+ 0x00000000, /* */
+
+ 0xc0026900,
+ 0x00000316,
+ 0x0000000e, /* VGT_VERTEX_REUSE_BLOCK_CNTL */
+ 0x00000010, /* */
+};
+
+const u32 evergreen_vs[] =
+{
+ 0x00000004,
+ 0x80800400,
+ 0x0000a03c,
+ 0x95000688,
+ 0x00004000,
+ 0x15200688,
+ 0x00000000,
+ 0x00000000,
+ 0x3c000000,
+ 0x67961001,
+ 0x00080000,
+ 0x00000000,
+ 0x1c000000,
+ 0x67961000,
+ 0x00000008,
+ 0x00000000,
+};
+
+const u32 evergreen_ps[] =
+{
+ 0x00000003,
+ 0xa00c0000,
+ 0x00000008,
+ 0x80400000,
+ 0x00000000,
+ 0x95200688,
+ 0x00380400,
+ 0x00146b10,
+ 0x00380000,
+ 0x20146b10,
+ 0x00380400,
+ 0x40146b00,
+ 0x80380000,
+ 0x60146b00,
+ 0x00000000,
+ 0x00000000,
+ 0x00000010,
+ 0x000d1000,
+ 0xb0800000,
+ 0x00000000,
+};
+
+const u32 evergreen_ps_size = ARRAY_SIZE(evergreen_ps);
+const u32 evergreen_vs_size = ARRAY_SIZE(evergreen_vs);
+const u32 evergreen_default_size = ARRAY_SIZE(evergreen_default_state);
diff --git a/drivers/gpu/drm/radeon/evergreen_blit_shaders.h b/drivers/gpu/drm/radeon/evergreen_blit_shaders.h
new file mode 100644
index 000000000000..bb8d6c751595
--- /dev/null
+++ b/drivers/gpu/drm/radeon/evergreen_blit_shaders.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2009 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 EVERGREEN_BLIT_SHADERS_H
+#define EVERGREEN_BLIT_SHADERS_H
+
+extern const u32 evergreen_ps[];
+extern const u32 evergreen_vs[];
+extern const u32 evergreen_default_state[];
+
+extern const u32 evergreen_ps_size, evergreen_vs_size;
+extern const u32 evergreen_default_size;
+
+#endif
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index 9b7532dd30f7..113c70cc8b39 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -412,6 +412,19 @@
#define SOFT_RESET_REGBB (1 << 22)
#define SOFT_RESET_ORB (1 << 23)
+/* display watermarks */
+#define DC_LB_MEMORY_SPLIT 0x6b0c
+#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 PIPE0_ARBITRATION_CONTROL3 0x0bf0
+# define LATENCY_WATERMARK_MASK(x) ((x) << 16)
+#define PIPE0_LATENCY_CONTROL 0x0bf4
+# define LATENCY_LOW_WATERMARK(x) ((x) << 0)
+# define LATENCY_HIGH_WATERMARK(x) ((x) << 16)
+
#define IH_RB_CNTL 0x3e00
# define IH_RB_ENABLE (1 << 0)
# define IH_IB_SIZE(x) ((x) << 1) /* log2 */
@@ -645,6 +658,8 @@
#define PACKET3_EVENT_WRITE_EOP 0x47
#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_RB_OFFSET 0x4B
#define PACKET3_ALU_PS_CONST_BUFFER_COPY 0x4C
#define PACKET3_ALU_VS_CONST_BUFFER_COPY 0x4D
@@ -802,6 +817,11 @@
#define SQ_ALU_CONST_CACHE_LS_14 0x28f78
#define SQ_ALU_CONST_CACHE_LS_15 0x28f7c
+#define PA_SC_SCREEN_SCISSOR_TL 0x28030
+#define PA_SC_GENERIC_SCISSOR_TL 0x28240
+#define PA_SC_WINDOW_SCISSOR_TL 0x28204
+#define VGT_PRIMITIVE_TYPE 0x8958
+
#define DB_DEPTH_CONTROL 0x28800
#define DB_DEPTH_VIEW 0x28008
#define DB_HTILE_DATA_BASE 0x28014
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index e59422320bb6..6d1540c0bfed 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -675,67 +675,6 @@ void r100_fence_ring_emit(struct radeon_device *rdev,
radeon_ring_write(rdev, RADEON_SW_INT_FIRE);
}
-int r100_wb_init(struct radeon_device *rdev)
-{
- int r;
-
- if (rdev->wb.wb_obj == NULL) {
- r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT,
- &rdev->wb.wb_obj);
- if (r) {
- dev_err(rdev->dev, "(%d) create WB buffer failed\n", r);
- return r;
- }
- r = radeon_bo_reserve(rdev->wb.wb_obj, false);
- if (unlikely(r != 0))
- return r;
- r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
- &rdev->wb.gpu_addr);
- if (r) {
- dev_err(rdev->dev, "(%d) pin WB buffer failed\n", r);
- radeon_bo_unreserve(rdev->wb.wb_obj);
- return r;
- }
- r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
- radeon_bo_unreserve(rdev->wb.wb_obj);
- if (r) {
- dev_err(rdev->dev, "(%d) map WB buffer failed\n", r);
- return r;
- }
- }
- WREG32(R_000774_SCRATCH_ADDR, rdev->wb.gpu_addr);
- WREG32(R_00070C_CP_RB_RPTR_ADDR,
- S_00070C_RB_RPTR_ADDR((rdev->wb.gpu_addr + 1024) >> 2));
- WREG32(R_000770_SCRATCH_UMSK, 0xff);
- return 0;
-}
-
-void r100_wb_disable(struct radeon_device *rdev)
-{
- WREG32(R_000770_SCRATCH_UMSK, 0);
-}
-
-void r100_wb_fini(struct radeon_device *rdev)
-{
- int r;
-
- r100_wb_disable(rdev);
- if (rdev->wb.wb_obj) {
- r = radeon_bo_reserve(rdev->wb.wb_obj, false);
- if (unlikely(r != 0)) {
- dev_err(rdev->dev, "(%d) can't finish WB\n", r);
- return;
- }
- radeon_bo_kunmap(rdev->wb.wb_obj);
- radeon_bo_unpin(rdev->wb.wb_obj);
- radeon_bo_unreserve(rdev->wb.wb_obj);
- radeon_bo_unref(&rdev->wb.wb_obj);
- rdev->wb.wb = NULL;
- rdev->wb.wb_obj = NULL;
- }
-}
-
int r100_copy_blit(struct radeon_device *rdev,
uint64_t src_offset,
uint64_t dst_offset,
@@ -996,20 +935,32 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
WREG32(0x718, pre_write_timer | (pre_write_limit << 28));
tmp = (REG_SET(RADEON_RB_BUFSZ, rb_bufsz) |
REG_SET(RADEON_RB_BLKSZ, rb_blksz) |
- REG_SET(RADEON_MAX_FETCH, max_fetch) |
- RADEON_RB_NO_UPDATE);
+ REG_SET(RADEON_MAX_FETCH, max_fetch));
#ifdef __BIG_ENDIAN
tmp |= RADEON_BUF_SWAP_32BIT;
#endif
- WREG32(RADEON_CP_RB_CNTL, tmp);
+ WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_NO_UPDATE);
/* Set ring address */
DRM_INFO("radeon: ring at 0x%016lX\n", (unsigned long)rdev->cp.gpu_addr);
WREG32(RADEON_CP_RB_BASE, rdev->cp.gpu_addr);
/* Force read & write ptr to 0 */
- WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA);
+ WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA | RADEON_RB_NO_UPDATE);
WREG32(RADEON_CP_RB_RPTR_WR, 0);
WREG32(RADEON_CP_RB_WPTR, 0);
+
+ /* set the wb address whether it's enabled or not */
+ WREG32(R_00070C_CP_RB_RPTR_ADDR,
+ S_00070C_RB_RPTR_ADDR((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) >> 2));
+ WREG32(R_000774_SCRATCH_ADDR, rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET);
+
+ if (rdev->wb.enabled)
+ WREG32(R_000770_SCRATCH_UMSK, 0xff);
+ else {
+ tmp |= RADEON_RB_NO_UPDATE;
+ WREG32(R_000770_SCRATCH_UMSK, 0);
+ }
+
WREG32(RADEON_CP_RB_CNTL, tmp);
udelay(10);
rdev->cp.rptr = RREG32(RADEON_CP_RB_RPTR);
@@ -1052,6 +1003,7 @@ void r100_cp_disable(struct radeon_device *rdev)
rdev->cp.ready = false;
WREG32(RADEON_CP_CSQ_MODE, 0);
WREG32(RADEON_CP_CSQ_CNTL, 0);
+ WREG32(R_000770_SCRATCH_UMSK, 0);
if (r100_gui_wait_for_idle(rdev)) {
printk(KERN_WARNING "Failed to wait GUI idle while "
"programming pipes. Bad things might happen.\n");
@@ -2318,6 +2270,9 @@ void r100_vram_init_sizes(struct radeon_device *rdev)
/* Fix for RN50, M6, M7 with 8/16/32(??) MBs of VRAM -
* Novell bug 204882 + along with lots of ubuntu ones
*/
+ if (rdev->mc.aper_size > config_aper_size)
+ config_aper_size = rdev->mc.aper_size;
+
if (config_aper_size > rdev->mc.real_vram_size)
rdev->mc.mc_vram_size = config_aper_size;
else
@@ -3737,6 +3692,12 @@ static int r100_startup(struct radeon_device *rdev)
if (r)
return r;
}
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
r100_irq_set(rdev);
rdev->config.r100.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -3746,9 +3707,6 @@ static int r100_startup(struct radeon_device *rdev)
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
return r;
}
- r = r100_wb_init(rdev);
- if (r)
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -3782,7 +3740,7 @@ int r100_resume(struct radeon_device *rdev)
int r100_suspend(struct radeon_device *rdev)
{
r100_cp_disable(rdev);
- r100_wb_disable(rdev);
+ radeon_wb_disable(rdev);
r100_irq_disable(rdev);
if (rdev->flags & RADEON_IS_PCI)
r100_pci_gart_disable(rdev);
@@ -3792,7 +3750,7 @@ int r100_suspend(struct radeon_device *rdev)
void r100_fini(struct radeon_device *rdev)
{
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_gem_fini(rdev);
if (rdev->flags & RADEON_IS_PCI)
@@ -3905,7 +3863,7 @@ int r100_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_irq_kms_fini(rdev);
if (rdev->flags & RADEON_IS_PCI)
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index c827738ad7dd..34527e600fe9 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -1332,6 +1332,12 @@ static int r300_startup(struct radeon_device *rdev)
if (r)
return r;
}
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
r100_irq_set(rdev);
rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -1341,9 +1347,6 @@ static int r300_startup(struct radeon_device *rdev)
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
return r;
}
- r = r100_wb_init(rdev);
- if (r)
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -1379,7 +1382,7 @@ int r300_resume(struct radeon_device *rdev)
int r300_suspend(struct radeon_device *rdev)
{
r100_cp_disable(rdev);
- r100_wb_disable(rdev);
+ radeon_wb_disable(rdev);
r100_irq_disable(rdev);
if (rdev->flags & RADEON_IS_PCIE)
rv370_pcie_gart_disable(rdev);
@@ -1391,7 +1394,7 @@ int r300_suspend(struct radeon_device *rdev)
void r300_fini(struct radeon_device *rdev)
{
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_gem_fini(rdev);
if (rdev->flags & RADEON_IS_PCIE)
@@ -1484,7 +1487,7 @@ int r300_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_irq_kms_fini(rdev);
if (rdev->flags & RADEON_IS_PCIE)
diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
index 59f7bccc5be0..c387346f93a9 100644
--- a/drivers/gpu/drm/radeon/r420.c
+++ b/drivers/gpu/drm/radeon/r420.c
@@ -248,6 +248,12 @@ static int r420_startup(struct radeon_device *rdev)
return r;
}
r420_pipes_init(rdev);
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
r100_irq_set(rdev);
rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -258,10 +264,6 @@ static int r420_startup(struct radeon_device *rdev)
return r;
}
r420_cp_errata_init(rdev);
- r = r100_wb_init(rdev);
- if (r) {
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
- }
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -302,7 +304,7 @@ int r420_suspend(struct radeon_device *rdev)
{
r420_cp_errata_fini(rdev);
r100_cp_disable(rdev);
- r100_wb_disable(rdev);
+ radeon_wb_disable(rdev);
r100_irq_disable(rdev);
if (rdev->flags & RADEON_IS_PCIE)
rv370_pcie_gart_disable(rdev);
@@ -314,7 +316,7 @@ int r420_suspend(struct radeon_device *rdev)
void r420_fini(struct radeon_device *rdev)
{
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_gem_fini(rdev);
if (rdev->flags & RADEON_IS_PCIE)
@@ -418,7 +420,7 @@ int r420_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_irq_kms_fini(rdev);
if (rdev->flags & RADEON_IS_PCIE)
diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c
index 1458dee902dd..3c8677f9e385 100644
--- a/drivers/gpu/drm/radeon/r520.c
+++ b/drivers/gpu/drm/radeon/r520.c
@@ -181,6 +181,12 @@ static int r520_startup(struct radeon_device *rdev)
if (r)
return r;
}
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
rs600_irq_set(rdev);
rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -190,9 +196,6 @@ static int r520_startup(struct radeon_device *rdev)
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
return r;
}
- r = r100_wb_init(rdev);
- if (r)
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -295,7 +298,7 @@ int r520_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_irq_kms_fini(rdev);
rv370_pcie_gart_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 7b65e4efe8af..33952a12f0a3 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -1608,8 +1608,11 @@ void r600_gpu_init(struct radeon_device *rdev)
rdev->config.r600.tiling_npipes = rdev->config.r600.max_tile_pipes;
rdev->config.r600.tiling_nbanks = 4 << ((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT);
tiling_config |= BANK_TILING((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT);
- tiling_config |= GROUP_SIZE(0);
- rdev->config.r600.tiling_group_size = 256;
+ tiling_config |= GROUP_SIZE((ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT);
+ if ((ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT)
+ rdev->config.r600.tiling_group_size = 512;
+ else
+ rdev->config.r600.tiling_group_size = 256;
tmp = (ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
if (tmp > 3) {
tiling_config |= ROW_TILING(3);
@@ -1920,6 +1923,7 @@ void r600_cp_stop(struct radeon_device *rdev)
{
rdev->mc.active_vram_size = rdev->mc.visible_vram_size;
WREG32(R_0086D8_CP_ME_CNTL, S_0086D8_CP_ME_HALT(1));
+ WREG32(SCRATCH_UMSK, 0);
}
int r600_init_microcode(struct radeon_device *rdev)
@@ -2152,7 +2156,7 @@ int r600_cp_resume(struct radeon_device *rdev)
/* Set ring buffer size */
rb_bufsz = drm_order(rdev->cp.ring_size / 8);
- tmp = RB_NO_UPDATE | (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+ tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
#ifdef __BIG_ENDIAN
tmp |= BUF_SWAP_32BIT;
#endif
@@ -2166,8 +2170,19 @@ int r600_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB_CNTL, tmp | RB_RPTR_WR_ENA);
WREG32(CP_RB_RPTR_WR, 0);
WREG32(CP_RB_WPTR, 0);
- WREG32(CP_RB_RPTR_ADDR, rdev->cp.gpu_addr & 0xFFFFFFFF);
- WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->cp.gpu_addr));
+
+ /* set the wb address whether it's enabled or not */
+ WREG32(CP_RB_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC);
+ WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
+ WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
+
+ if (rdev->wb.enabled)
+ WREG32(SCRATCH_UMSK, 0xff);
+ else {
+ tmp |= RB_NO_UPDATE;
+ WREG32(SCRATCH_UMSK, 0);
+ }
+
mdelay(1);
WREG32(CP_RB_CNTL, tmp);
@@ -2219,9 +2234,10 @@ void r600_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] = SCRATCH_REG0 + (i * 4);
+ rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4);
}
}
@@ -2265,88 +2281,34 @@ int r600_ring_test(struct radeon_device *rdev)
return r;
}
-void r600_wb_disable(struct radeon_device *rdev)
-{
- int r;
-
- WREG32(SCRATCH_UMSK, 0);
- if (rdev->wb.wb_obj) {
- r = radeon_bo_reserve(rdev->wb.wb_obj, false);
- if (unlikely(r != 0))
- return;
- radeon_bo_kunmap(rdev->wb.wb_obj);
- radeon_bo_unpin(rdev->wb.wb_obj);
- radeon_bo_unreserve(rdev->wb.wb_obj);
- }
-}
-
-void r600_wb_fini(struct radeon_device *rdev)
-{
- r600_wb_disable(rdev);
- if (rdev->wb.wb_obj) {
- radeon_bo_unref(&rdev->wb.wb_obj);
- rdev->wb.wb = NULL;
- rdev->wb.wb_obj = NULL;
- }
-}
-
-int r600_wb_enable(struct radeon_device *rdev)
-{
- int r;
-
- if (rdev->wb.wb_obj == NULL) {
- r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT, &rdev->wb.wb_obj);
- if (r) {
- dev_warn(rdev->dev, "(%d) create WB bo failed\n", r);
- return r;
- }
- r = radeon_bo_reserve(rdev->wb.wb_obj, false);
- if (unlikely(r != 0)) {
- r600_wb_fini(rdev);
- return r;
- }
- r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
- &rdev->wb.gpu_addr);
- if (r) {
- radeon_bo_unreserve(rdev->wb.wb_obj);
- dev_warn(rdev->dev, "(%d) pin WB bo failed\n", r);
- r600_wb_fini(rdev);
- return r;
- }
- r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
- radeon_bo_unreserve(rdev->wb.wb_obj);
- if (r) {
- dev_warn(rdev->dev, "(%d) map WB bo failed\n", r);
- r600_wb_fini(rdev);
- return r;
- }
- }
- WREG32(SCRATCH_ADDR, (rdev->wb.gpu_addr >> 8) & 0xFFFFFFFF);
- WREG32(CP_RB_RPTR_ADDR, (rdev->wb.gpu_addr + 1024) & 0xFFFFFFFC);
- WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + 1024) & 0xFF);
- WREG32(SCRATCH_UMSK, 0xff);
- return 0;
-}
-
void r600_fence_ring_emit(struct radeon_device *rdev,
struct radeon_fence *fence)
{
- /* Also consider EVENT_WRITE_EOP. it handles the interrupts + timestamps + events */
-
- radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
- radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
- /* wait for 3D idle clean */
- radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
- radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
- radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
- /* Emit fence sequence & fire IRQ */
- radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
- radeon_ring_write(rdev, ((rdev->fence_drv.scratch_reg - PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
- radeon_ring_write(rdev, fence->seq);
- /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */
- radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
- radeon_ring_write(rdev, RB_INT_STAT);
+ if (rdev->wb.use_event) {
+ u64 addr = rdev->wb.gpu_addr + R600_WB_EVENT_OFFSET +
+ (u64)(rdev->fence_drv.scratch_reg - rdev->scratch.reg_base);
+ /* EVENT_WRITE_EOP - flush caches, send int */
+ radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
+ radeon_ring_write(rdev, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5));
+ radeon_ring_write(rdev, addr & 0xffffffff);
+ radeon_ring_write(rdev, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
+ radeon_ring_write(rdev, fence->seq);
+ radeon_ring_write(rdev, 0);
+ } else {
+ radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
+ radeon_ring_write(rdev, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT) | EVENT_INDEX(0));
+ /* wait for 3D idle clean */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
+ radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
+ /* Emit fence sequence & fire IRQ */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(rdev, ((rdev->fence_drv.scratch_reg - PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
+ radeon_ring_write(rdev, fence->seq);
+ /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */
+ radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
+ radeon_ring_write(rdev, RB_INT_STAT);
+ }
}
int r600_copy_blit(struct radeon_device *rdev,
@@ -2428,19 +2390,12 @@ int r600_startup(struct radeon_device *rdev)
rdev->asic->copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
- /* pin copy shader into vram */
- if (rdev->r600_blit.shader_obj) {
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (unlikely(r != 0))
- return r;
- r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
- &rdev->r600_blit.shader_gpu_addr);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
- if (r) {
- dev_err(rdev->dev, "(%d) pin blit object failed\n", r);
- return r;
- }
- }
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
r = r600_irq_init(rdev);
if (r) {
@@ -2459,8 +2414,7 @@ int r600_startup(struct radeon_device *rdev)
r = r600_cp_resume(rdev);
if (r)
return r;
- /* write back buffer are not vital so don't worry about failure */
- r600_wb_enable(rdev);
+
return 0;
}
@@ -2519,7 +2473,7 @@ int r600_suspend(struct radeon_device *rdev)
r600_cp_stop(rdev);
rdev->cp.ready = false;
r600_irq_suspend(rdev);
- r600_wb_disable(rdev);
+ radeon_wb_disable(rdev);
r600_pcie_gart_disable(rdev);
/* unpin shaders bo */
if (rdev->r600_blit.shader_obj) {
@@ -2616,8 +2570,8 @@ int r600_init(struct radeon_device *rdev)
if (r) {
dev_err(rdev->dev, "disabling GPU acceleration\n");
r600_cp_fini(rdev);
- r600_wb_fini(rdev);
r600_irq_fini(rdev);
+ radeon_wb_fini(rdev);
radeon_irq_kms_fini(rdev);
r600_pcie_gart_fini(rdev);
rdev->accel_working = false;
@@ -2647,8 +2601,8 @@ void r600_fini(struct radeon_device *rdev)
r600_audio_fini(rdev);
r600_blit_fini(rdev);
r600_cp_fini(rdev);
- r600_wb_fini(rdev);
r600_irq_fini(rdev);
+ radeon_wb_fini(rdev);
radeon_irq_kms_fini(rdev);
r600_pcie_gart_fini(rdev);
radeon_agp_fini(rdev);
@@ -2983,10 +2937,13 @@ int r600_irq_init(struct radeon_device *rdev)
ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE |
IH_WPTR_OVERFLOW_CLEAR |
(rb_bufsz << 1));
- /* WPTR writeback, not yet */
- /*ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE;*/
- WREG32(IH_RB_WPTR_ADDR_LO, 0);
- WREG32(IH_RB_WPTR_ADDR_HI, 0);
+
+ 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);
@@ -3070,6 +3027,7 @@ int r600_irq_set(struct radeon_device *rdev)
if (rdev->irq.sw_int) {
DRM_DEBUG("r600_irq_set: sw int\n");
cp_int_cntl |= RB_INT_ENABLE;
+ cp_int_cntl |= TIME_STAMP_INT_ENABLE;
}
if (rdev->irq.crtc_vblank_int[0]) {
DRM_DEBUG("r600_irq_set: vblank 0\n");
@@ -3244,8 +3202,10 @@ static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
{
u32 wptr, tmp;
- /* XXX use writeback */
- wptr = RREG32(IH_RB_WPTR);
+ if (rdev->wb.enabled)
+ wptr = 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
@@ -3433,6 +3393,7 @@ restart_ih:
break;
case 181: /* CP EOP event */
DRM_DEBUG("IH: CP EOP\n");
+ radeon_fence_process(rdev);
break;
case 233: /* GUI IDLE */
DRM_DEBUG("IH: CP EOP\n");
diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c
index 3473c00781ff..8362974ef41a 100644
--- a/drivers/gpu/drm/radeon/r600_blit_kms.c
+++ b/drivers/gpu/drm/radeon/r600_blit_kms.c
@@ -472,9 +472,10 @@ int r600_blit_init(struct radeon_device *rdev)
u32 packet2s[16];
int num_packet2s = 0;
- /* don't reinitialize blit */
+ /* pin copy shader into vram if already initialized */
if (rdev->r600_blit.shader_obj)
- return 0;
+ goto done;
+
mutex_init(&rdev->r600_blit.mutex);
rdev->r600_blit.state_offset = 0;
@@ -532,6 +533,18 @@ int r600_blit_init(struct radeon_device *rdev)
memcpy(ptr + rdev->r600_blit.ps_offset, r6xx_ps, r6xx_ps_size * 4);
radeon_bo_kunmap(rdev->r600_blit.shader_obj);
radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+
+done:
+ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+ if (unlikely(r != 0))
+ return r;
+ r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
+ &rdev->r600_blit.shader_gpu_addr);
+ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+ if (r) {
+ dev_err(rdev->dev, "(%d) pin blit object failed\n", r);
+ return r;
+ }
rdev->mc.active_vram_size = rdev->mc.real_vram_size;
return 0;
}
@@ -554,7 +567,7 @@ void r600_blit_fini(struct radeon_device *rdev)
radeon_bo_unref(&rdev->r600_blit.shader_obj);
}
-int r600_vb_ib_get(struct radeon_device *rdev)
+static int r600_vb_ib_get(struct radeon_device *rdev)
{
int r;
r = radeon_ib_get(rdev, &rdev->r600_blit.vb_ib);
@@ -568,7 +581,7 @@ int r600_vb_ib_get(struct radeon_device *rdev)
return 0;
}
-void r600_vb_ib_put(struct radeon_device *rdev)
+static void r600_vb_ib_put(struct radeon_device *rdev)
{
radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence);
radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
@@ -650,8 +663,8 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
int src_x = src_gpu_addr & 255;
int dst_x = dst_gpu_addr & 255;
int h = 1;
- src_gpu_addr = src_gpu_addr & ~255;
- dst_gpu_addr = dst_gpu_addr & ~255;
+ src_gpu_addr = src_gpu_addr & ~255ULL;
+ dst_gpu_addr = dst_gpu_addr & ~255ULL;
if (!src_x && !dst_x) {
h = (cur_size / max_bytes);
@@ -672,17 +685,6 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
WARN_ON(1);
-
-#if 0
- r600_vb_ib_put(rdev);
-
- r600_nomm_put_vb(dev);
- r600_nomm_get_vb(dev);
- if (!dev_priv->blit_vb)
- return;
- set_shaders(dev);
- vb = r600_nomm_get_vb_ptr(dev);
-#endif
}
vb[0] = i2f(dst_x);
@@ -744,8 +746,8 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
int src_x = (src_gpu_addr & 255);
int dst_x = (dst_gpu_addr & 255);
int h = 1;
- src_gpu_addr = src_gpu_addr & ~255;
- dst_gpu_addr = dst_gpu_addr & ~255;
+ src_gpu_addr = src_gpu_addr & ~255ULL;
+ dst_gpu_addr = dst_gpu_addr & ~255ULL;
if (!src_x && !dst_x) {
h = (cur_size / max_bytes);
@@ -767,17 +769,6 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) {
WARN_ON(1);
}
-#if 0
- if ((rdev->blit_vb->used + 48) > rdev->blit_vb->total) {
- r600_nomm_put_vb(dev);
- r600_nomm_get_vb(dev);
- if (!rdev->blit_vb)
- return;
-
- set_shaders(dev);
- vb = r600_nomm_get_vb_ptr(dev);
- }
-#endif
vb[0] = i2f(dst_x / 4);
vb[1] = 0;
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 250a3a918193..7b294c127c5f 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -170,6 +170,7 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
struct r600_cs_track *track = p->track;
u32 bpe = 0, pitch, slice_tile_max, size, tmp, height, pitch_align;
volatile u32 *ib = p->ib->ptr;
+ unsigned array_mode;
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");
@@ -185,12 +186,12 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
/* pitch is the number of 8x8 tiles per row */
pitch = G_028060_PITCH_TILE_MAX(track->cb_color_size[i]) + 1;
slice_tile_max = G_028060_SLICE_TILE_MAX(track->cb_color_size[i]) + 1;
- height = size / (pitch * 8 * bpe);
+ slice_tile_max *= 64;
+ height = slice_tile_max / (pitch * 8);
if (height > 8192)
height = 8192;
- if (height > 7)
- height &= ~0x7;
- switch (G_0280A0_ARRAY_MODE(track->cb_color_info[i])) {
+ array_mode = G_0280A0_ARRAY_MODE(track->cb_color_info[i]);
+ switch (array_mode) {
case V_0280A0_ARRAY_LINEAR_GENERAL:
/* technically height & 0x7 */
break;
@@ -214,6 +215,9 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
__func__, __LINE__, pitch);
return -EINVAL;
}
+ /* avoid breaking userspace */
+ if (height > 7)
+ height &= ~0x7;
if (!IS_ALIGNED(height, 8)) {
dev_warn(p->dev, "%s:%d cb height (%d) invalid\n",
__func__, __LINE__, height);
@@ -222,13 +226,13 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
break;
case V_0280A0_ARRAY_2D_TILED_THIN1:
pitch_align = max((u32)track->nbanks,
- (u32)(((track->group_size / 8) / (bpe * track->nsamples)) * track->nbanks));
+ (u32)(((track->group_size / 8) / (bpe * track->nsamples)) * track->nbanks)) / 8;
if (!IS_ALIGNED(pitch, pitch_align)) {
dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n",
__func__, __LINE__, pitch);
return -EINVAL;
}
- if (!IS_ALIGNED((height / 8), track->nbanks)) {
+ if (!IS_ALIGNED((height / 8), track->npipes)) {
dev_warn(p->dev, "%s:%d cb height (%d) invalid\n",
__func__, __LINE__, height);
return -EINVAL;
@@ -243,8 +247,18 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
/* check offset */
tmp = height * pitch * 8 * bpe;
if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) {
- dev_warn(p->dev, "%s offset[%d] %d too big\n", __func__, i, track->cb_color_bo_offset[i]);
- return -EINVAL;
+ if (array_mode == V_0280A0_ARRAY_LINEAR_GENERAL) {
+ /* the initial DDX does bad things with the CB size occasionally */
+ /* it rounds up height too far for slice tile max but the BO is smaller */
+ tmp = (height - 7) * 8 * bpe;
+ if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) {
+ dev_warn(p->dev, "%s offset[%d] %d %d %lu too big\n", __func__, i, track->cb_color_bo_offset[i], tmp, radeon_bo_size(track->cb_color_bo[i]));
+ return -EINVAL;
+ }
+ } else {
+ dev_warn(p->dev, "%s offset[%d] %d %d %lu too big\n", __func__, i, track->cb_color_bo_offset[i], tmp, radeon_bo_size(track->cb_color_bo[i]));
+ return -EINVAL;
+ }
}
if (!IS_ALIGNED(track->cb_color_bo_offset[i], track->group_size)) {
dev_warn(p->dev, "%s offset[%d] %d not aligned\n", __func__, i, track->cb_color_bo_offset[i]);
@@ -361,13 +375,13 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
break;
case V_028010_ARRAY_2D_TILED_THIN1:
pitch_align = max((u32)track->nbanks,
- (u32)(((track->group_size / 8) / bpe) * track->nbanks));
+ (u32)(((track->group_size / 8) / bpe) * track->nbanks)) / 8;
if (!IS_ALIGNED(pitch, pitch_align)) {
dev_warn(p->dev, "%s:%d db pitch (%d) invalid\n",
__func__, __LINE__, pitch);
return -EINVAL;
}
- if ((height / 8) & (track->nbanks - 1)) {
+ if (!IS_ALIGNED((height / 8), track->npipes)) {
dev_warn(p->dev, "%s:%d db height (%d) invalid\n",
__func__, __LINE__, height);
return -EINVAL;
@@ -1138,7 +1152,7 @@ static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 i
break;
case V_038000_ARRAY_2D_TILED_THIN1:
pitch_align = max((u32)track->nbanks,
- (u32)(((track->group_size / 8) / bpe) * track->nbanks));
+ (u32)(((track->group_size / 8) / bpe) * track->nbanks)) / 8;
if (!IS_ALIGNED(pitch, pitch_align)) {
dev_warn(p->dev, "%s:%d tex pitch (%d) invalid\n",
__func__, __LINE__, pitch);
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 858a1920c0d7..966a793e225b 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -474,6 +474,7 @@
#define VGT_VERTEX_REUSE_BLOCK_CNTL 0x28C58
#define VTX_REUSE_DEPTH_MASK 0x000000FF
#define VGT_EVENT_INITIATOR 0x28a90
+# define CACHE_FLUSH_AND_INV_EVENT_TS (0x14 << 0)
# define CACHE_FLUSH_AND_INV_EVENT (0x16 << 0)
#define VM_CONTEXT0_CNTL 0x1410
@@ -775,7 +776,27 @@
#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 - TS events
+ */
#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_ONE_REG_WRITE 0x57
#define PACKET3_SET_CONFIG_REG 0x68
#define PACKET3_SET_CONFIG_REG_OFFSET 0x00008000
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 9ff38c99a6ea..73f600d39ad4 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -88,7 +88,6 @@ extern int radeon_benchmarking;
extern int radeon_testing;
extern int radeon_connector_table;
extern int radeon_tv;
-extern int radeon_new_pll;
extern int radeon_audio;
extern int radeon_disp_priority;
extern int radeon_hw_i2c;
@@ -366,6 +365,7 @@ bool radeon_atombios_sideport_present(struct radeon_device *rdev);
*/
struct radeon_scratch {
unsigned num_reg;
+ uint32_t reg_base;
bool free[32];
uint32_t reg[32];
};
@@ -594,8 +594,15 @@ struct radeon_wb {
struct radeon_bo *wb_obj;
volatile uint32_t *wb;
uint64_t gpu_addr;
+ bool enabled;
+ bool use_event;
};
+#define RADEON_WB_SCRATCH_OFFSET 0
+#define RADEON_WB_CP_RPTR_OFFSET 1024
+#define R600_WB_IH_WPTR_OFFSET 2048
+#define R600_WB_EVENT_OFFSET 3072
+
/**
* struct radeon_pm - power management datas
* @max_bandwidth: maximum bandwidth the gpu has (MByte/s)
@@ -1124,6 +1131,12 @@ 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,
int size_bytes);
+/* evergreen blit */
+int evergreen_blit_prepare_copy(struct radeon_device *rdev, int size_bytes);
+void evergreen_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence);
+void evergreen_kms_blit_copy(struct radeon_device *rdev,
+ u64 src_gpu_addr, u64 dst_gpu_addr,
+ int size_bytes);
static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
{
@@ -1341,6 +1354,9 @@ extern void radeon_update_bandwidth_info(struct radeon_device *rdev);
extern void radeon_update_display_priority(struct radeon_device *rdev);
extern bool radeon_boot_test_post_card(struct radeon_device *rdev);
extern void radeon_scratch_init(struct radeon_device *rdev);
+extern void radeon_wb_fini(struct radeon_device *rdev);
+extern int radeon_wb_init(struct radeon_device *rdev);
+extern void radeon_wb_disable(struct radeon_device *rdev);
extern void radeon_surface_init(struct radeon_device *rdev);
extern int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data);
extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable);
@@ -1425,9 +1441,6 @@ extern int r600_pcie_gart_init(struct radeon_device *rdev);
extern void r600_pcie_gart_tlb_flush(struct radeon_device *rdev);
extern int r600_ib_test(struct radeon_device *rdev);
extern int r600_ring_test(struct radeon_device *rdev);
-extern void r600_wb_fini(struct radeon_device *rdev);
-extern int r600_wb_enable(struct radeon_device *rdev);
-extern void r600_wb_disable(struct radeon_device *rdev);
extern void r600_scratch_init(struct radeon_device *rdev);
extern int r600_blit_init(struct radeon_device *rdev);
extern void r600_blit_fini(struct radeon_device *rdev);
@@ -1465,6 +1478,8 @@ extern void r700_cp_stop(struct radeon_device *rdev);
extern void r700_cp_fini(struct radeon_device *rdev);
extern void evergreen_disable_interrupt_state(struct radeon_device *rdev);
extern int evergreen_irq_set(struct radeon_device *rdev);
+extern int evergreen_blit_init(struct radeon_device *rdev);
+extern void evergreen_blit_fini(struct radeon_device *rdev);
/* radeon_acpi.c */
#if defined(CONFIG_ACPI)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index 25e1dd197791..64fb89ecbf74 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -726,9 +726,9 @@ static struct radeon_asic evergreen_asic = {
.get_vblank_counter = &evergreen_get_vblank_counter,
.fence_ring_emit = &r600_fence_ring_emit,
.cs_parse = &evergreen_cs_parse,
- .copy_blit = NULL,
- .copy_dma = NULL,
- .copy = NULL,
+ .copy_blit = &evergreen_copy_blit,
+ .copy_dma = &evergreen_copy_blit,
+ .copy = &evergreen_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,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index a5aff755f0d2..740988244143 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -108,9 +108,6 @@ 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);
void r100_vram_init_sizes(struct radeon_device *rdev);
-void r100_wb_disable(struct radeon_device *rdev);
-void r100_wb_fini(struct radeon_device *rdev);
-int r100_wb_init(struct radeon_device *rdev);
int r100_cp_reset(struct radeon_device *rdev);
void r100_vga_render_disable(struct radeon_device *rdev);
void r100_restore_sanity(struct radeon_device *rdev);
@@ -257,11 +254,6 @@ void r600_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
int r600_cs_parse(struct radeon_cs_parser *p);
void r600_fence_ring_emit(struct radeon_device *rdev,
struct radeon_fence *fence);
-int r600_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_pages,
- struct radeon_fence *fence);
int r600_irq_process(struct radeon_device *rdev);
int r600_irq_set(struct radeon_device *rdev);
bool r600_gpu_is_lockup(struct radeon_device *rdev);
@@ -307,6 +299,9 @@ int evergreen_resume(struct radeon_device *rdev);
bool evergreen_gpu_is_lockup(struct radeon_device *rdev);
int evergreen_asic_reset(struct radeon_device *rdev);
void evergreen_bandwidth_update(struct radeon_device *rdev);
+int evergreen_copy_blit(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_pages, struct radeon_fence *fence);
void evergreen_hpd_init(struct radeon_device *rdev);
void evergreen_hpd_fini(struct radeon_device *rdev);
bool evergreen_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 8e43ddae70cc..04cac7ec9039 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -1112,8 +1112,7 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
* pre-DCE 3.0 r6xx hardware. This might need to be adjusted per
* family.
*/
- if (!radeon_new_pll)
- p1pll->pll_out_min = 64800;
+ p1pll->pll_out_min = 64800;
}
p1pll->pll_in_min =
@@ -1277,36 +1276,27 @@ bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
return false;
}
-static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
- radeon_encoder
- *encoder,
- int id)
+bool radeon_atombios_get_ppll_ss_info(struct radeon_device *rdev,
+ struct radeon_atom_ss *ss,
+ int id)
{
- struct drm_device *dev = encoder->base.dev;
- struct radeon_device *rdev = dev->dev_private;
struct radeon_mode_info *mode_info = &rdev->mode_info;
int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info);
- uint16_t data_offset;
+ uint16_t data_offset, size;
struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info;
uint8_t frev, crev;
- struct radeon_atom_ss *ss = NULL;
- int i;
-
- if (id > ATOM_MAX_SS_ENTRY)
- return NULL;
+ int i, num_indices;
- if (atom_parse_data_header(mode_info->atom_context, index, NULL,
+ memset(ss, 0, sizeof(struct radeon_atom_ss));
+ if (atom_parse_data_header(mode_info->atom_context, index, &size,
&frev, &crev, &data_offset)) {
ss_info =
(struct _ATOM_SPREAD_SPECTRUM_INFO *)(mode_info->atom_context->bios + data_offset);
- ss =
- kzalloc(sizeof(struct radeon_atom_ss), GFP_KERNEL);
-
- if (!ss)
- return NULL;
+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
+ sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT);
- for (i = 0; i < ATOM_MAX_SS_ENTRY; i++) {
+ for (i = 0; i < num_indices; i++) {
if (ss_info->asSS_Info[i].ucSS_Id == id) {
ss->percentage =
le16_to_cpu(ss_info->asSS_Info[i].usSpreadSpectrumPercentage);
@@ -1315,11 +1305,88 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
ss->delay = ss_info->asSS_Info[i].ucSS_Delay;
ss->range = ss_info->asSS_Info[i].ucSS_Range;
ss->refdiv = ss_info->asSS_Info[i].ucRecommendedRef_Div;
- break;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+union asic_ss_info {
+ struct _ATOM_ASIC_INTERNAL_SS_INFO info;
+ struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 info_2;
+ struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3;
+};
+
+bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
+ struct radeon_atom_ss *ss,
+ int id, u32 clock)
+{
+ struct radeon_mode_info *mode_info = &rdev->mode_info;
+ int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+ uint16_t data_offset, size;
+ union asic_ss_info *ss_info;
+ uint8_t frev, crev;
+ int i, num_indices;
+
+ memset(ss, 0, sizeof(struct radeon_atom_ss));
+ if (atom_parse_data_header(mode_info->atom_context, index, &size,
+ &frev, &crev, &data_offset)) {
+
+ ss_info =
+ (union asic_ss_info *)(mode_info->atom_context->bios + data_offset);
+
+ switch (frev) {
+ case 1:
+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
+ sizeof(ATOM_ASIC_SS_ASSIGNMENT);
+
+ for (i = 0; i < num_indices; i++) {
+ if ((ss_info->info.asSpreadSpectrum[i].ucClockIndication == id) &&
+ (clock <= ss_info->info.asSpreadSpectrum[i].ulTargetClockRange)) {
+ ss->percentage =
+ le16_to_cpu(ss_info->info.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
+ ss->type = ss_info->info.asSpreadSpectrum[i].ucSpreadSpectrumMode;
+ ss->rate = le16_to_cpu(ss_info->info.asSpreadSpectrum[i].usSpreadRateInKhz);
+ return true;
+ }
+ }
+ break;
+ case 2:
+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
+ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2);
+ for (i = 0; i < num_indices; i++) {
+ if ((ss_info->info_2.asSpreadSpectrum[i].ucClockIndication == id) &&
+ (clock <= ss_info->info_2.asSpreadSpectrum[i].ulTargetClockRange)) {
+ ss->percentage =
+ le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
+ ss->type = ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode;
+ ss->rate = le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz);
+ return true;
+ }
}
+ break;
+ case 3:
+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
+ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3);
+ for (i = 0; i < num_indices; i++) {
+ if ((ss_info->info_3.asSpreadSpectrum[i].ucClockIndication == id) &&
+ (clock <= ss_info->info_3.asSpreadSpectrum[i].ulTargetClockRange)) {
+ ss->percentage =
+ le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
+ ss->type = ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode;
+ ss->rate = le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz);
+ return true;
+ }
+ }
+ break;
+ default:
+ DRM_ERROR("Unsupported ASIC_InternalSS_Info table: %d %d\n", frev, crev);
+ break;
}
+
}
- return ss;
+ return false;
}
union lvds_info {
@@ -1371,7 +1438,7 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth);
lvds->panel_pwr_delay =
le16_to_cpu(lvds_info->info.usOffDelayInMs);
- lvds->lvds_misc = lvds_info->info.ucLVDS_Misc;
+ lvds->lcd_misc = lvds_info->info.ucLVDS_Misc;
misc = le16_to_cpu(lvds_info->info.sLCDTiming.susModeMiscInfo.usAccess);
if (misc & ATOM_VSYNC_POLARITY)
@@ -1388,19 +1455,7 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
/* set crtc values */
drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V);
- lvds->ss = radeon_atombios_get_ss_info(encoder, lvds_info->info.ucSS_Id);
-
- if (ASIC_IS_AVIVO(rdev)) {
- if (radeon_new_pll == 0)
- lvds->pll_algo = PLL_ALGO_LEGACY;
- else
- lvds->pll_algo = PLL_ALGO_NEW;
- } else {
- if (radeon_new_pll == 1)
- lvds->pll_algo = PLL_ALGO_NEW;
- else
- lvds->pll_algo = PLL_ALGO_LEGACY;
- }
+ lvds->lcd_ss_id = lvds_info->info.ucSS_Id;
encoder->native_mode = lvds->native_mode;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index ecc1a8fafbfd..4dac4b0a02ee 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -326,6 +326,34 @@ int radeon_connector_set_property(struct drm_connector *connector, struct drm_pr
}
}
+ if (property == rdev->mode_info.underscan_hborder_property) {
+ /* need to find digital encoder on connector */
+ encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
+ if (!encoder)
+ return 0;
+
+ radeon_encoder = to_radeon_encoder(encoder);
+
+ if (radeon_encoder->underscan_hborder != val) {
+ radeon_encoder->underscan_hborder = val;
+ radeon_property_change_mode(&radeon_encoder->base);
+ }
+ }
+
+ if (property == rdev->mode_info.underscan_vborder_property) {
+ /* need to find digital encoder on connector */
+ encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
+ if (!encoder)
+ return 0;
+
+ radeon_encoder = to_radeon_encoder(encoder);
+
+ if (radeon_encoder->underscan_vborder != val) {
+ radeon_encoder->underscan_vborder = val;
+ radeon_property_change_mode(&radeon_encoder->base);
+ }
+ }
+
if (property == rdev->mode_info.tv_std_property) {
encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TVDAC);
if (!encoder) {
@@ -635,6 +663,11 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
ret = connector_status_connected;
}
} else {
+
+ /* if we aren't forcing don't do destructive polling */
+ if (!force)
+ return connector->status;
+
if (radeon_connector->dac_load_detect && encoder) {
encoder_funcs = encoder->helper_private;
ret = encoder_funcs->detect(encoder, connector);
@@ -822,6 +855,11 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if ((ret == connector_status_connected) && (radeon_connector->use_digital == true))
goto out;
+ if (!force) {
+ ret = connector->status;
+ goto out;
+ }
+
/* find analog encoder */
if (radeon_connector->dac_load_detect) {
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
@@ -1153,10 +1191,17 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property,
1);
- if (ASIC_IS_AVIVO(rdev))
+ if (ASIC_IS_AVIVO(rdev)) {
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_property,
UNDERSCAN_AUTO);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_hborder_property,
+ 0);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_vborder_property,
+ 0);
+ }
if (connector_type == DRM_MODE_CONNECTOR_DVII) {
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
@@ -1181,10 +1226,17 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property,
1);
- if (ASIC_IS_AVIVO(rdev))
+ if (ASIC_IS_AVIVO(rdev)) {
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_property,
UNDERSCAN_AUTO);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_hborder_property,
+ 0);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_vborder_property,
+ 0);
+ }
subpixel_order = SubPixelHorizontalRGB;
break;
case DRM_MODE_CONNECTOR_DisplayPort:
@@ -1212,10 +1264,17 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property,
1);
- if (ASIC_IS_AVIVO(rdev))
+ if (ASIC_IS_AVIVO(rdev)) {
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_property,
UNDERSCAN_AUTO);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_hborder_property,
+ 0);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_vborder_property,
+ 0);
+ }
break;
case DRM_MODE_CONNECTOR_SVIDEO:
case DRM_MODE_CONNECTOR_Composite:
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index 3eef567b0421..017ac54920fb 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -118,22 +118,25 @@ static void radeon_show_cursor(struct drm_crtc *crtc)
}
static void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj,
- uint32_t gpu_addr)
+ uint64_t gpu_addr)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct radeon_device *rdev = crtc->dev->dev_private;
if (ASIC_IS_DCE4(rdev)) {
- WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, 0);
- WREG32(EVERGREEN_CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, gpu_addr);
+ WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
+ upper_32_bits(gpu_addr));
+ WREG32(EVERGREEN_CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset,
+ gpu_addr & 0xffffffff);
} else if (ASIC_IS_AVIVO(rdev)) {
if (rdev->family >= CHIP_RV770) {
if (radeon_crtc->crtc_id)
- WREG32(R700_D2CUR_SURFACE_ADDRESS_HIGH, 0);
+ WREG32(R700_D2CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr));
else
- WREG32(R700_D1CUR_SURFACE_ADDRESS_HIGH, 0);
+ WREG32(R700_D1CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr));
}
- WREG32(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, gpu_addr);
+ WREG32(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset,
+ gpu_addr & 0xffffffff);
} else {
radeon_crtc->legacy_cursor_offset = gpu_addr - radeon_crtc->legacy_display_base_addr;
/* offset is from DISP(2)_BASE_ADDRESS */
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 256d204a6d24..8adfedfe547f 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -117,9 +117,10 @@ void radeon_scratch_init(struct radeon_device *rdev)
} else {
rdev->scratch.num_reg = 7;
}
+ rdev->scratch.reg_base = RADEON_SCRATCH_REG0;
for (i = 0; i < rdev->scratch.num_reg; i++) {
rdev->scratch.free[i] = true;
- rdev->scratch.reg[i] = RADEON_SCRATCH_REG0 + (i * 4);
+ rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4);
}
}
@@ -149,6 +150,86 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg)
}
}
+void radeon_wb_disable(struct radeon_device *rdev)
+{
+ int r;
+
+ if (rdev->wb.wb_obj) {
+ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
+ if (unlikely(r != 0))
+ return;
+ radeon_bo_kunmap(rdev->wb.wb_obj);
+ radeon_bo_unpin(rdev->wb.wb_obj);
+ radeon_bo_unreserve(rdev->wb.wb_obj);
+ }
+ rdev->wb.enabled = false;
+}
+
+void radeon_wb_fini(struct radeon_device *rdev)
+{
+ radeon_wb_disable(rdev);
+ if (rdev->wb.wb_obj) {
+ radeon_bo_unref(&rdev->wb.wb_obj);
+ rdev->wb.wb = NULL;
+ rdev->wb.wb_obj = NULL;
+ }
+}
+
+int radeon_wb_init(struct radeon_device *rdev)
+{
+ int r;
+
+ if (rdev->wb.wb_obj == NULL) {
+ r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
+ RADEON_GEM_DOMAIN_GTT, &rdev->wb.wb_obj);
+ if (r) {
+ dev_warn(rdev->dev, "(%d) create WB bo failed\n", r);
+ return r;
+ }
+ }
+ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
+ if (unlikely(r != 0)) {
+ radeon_wb_fini(rdev);
+ return r;
+ }
+ r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
+ &rdev->wb.gpu_addr);
+ if (r) {
+ radeon_bo_unreserve(rdev->wb.wb_obj);
+ dev_warn(rdev->dev, "(%d) pin WB bo failed\n", r);
+ radeon_wb_fini(rdev);
+ return r;
+ }
+ r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
+ radeon_bo_unreserve(rdev->wb.wb_obj);
+ if (r) {
+ dev_warn(rdev->dev, "(%d) map WB bo failed\n", r);
+ radeon_wb_fini(rdev);
+ return r;
+ }
+
+ /* disable event_write fences */
+ rdev->wb.use_event = false;
+ /* disabled via module param */
+ if (radeon_no_wb == 1)
+ rdev->wb.enabled = false;
+ else {
+ /* often unreliable on AGP */
+ if (rdev->flags & RADEON_IS_AGP) {
+ rdev->wb.enabled = false;
+ } else {
+ rdev->wb.enabled = true;
+ /* event_write fences are only available on r600+ */
+ if (rdev->family >= CHIP_R600)
+ rdev->wb.use_event = true;
+ }
+ }
+
+ dev_info(rdev->dev, "WB %sabled\n", rdev->wb.enabled ? "en" : "dis");
+
+ return 0;
+}
+
/**
* radeon_vram_location - try to find VRAM location
* @rdev: radeon device structure holding all necessary informations
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index b92d2f2fcbed..0383631da69c 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -454,13 +454,13 @@ static inline uint32_t radeon_div(uint64_t n, uint32_t d)
return n;
}
-static void radeon_compute_pll_legacy(struct radeon_pll *pll,
- uint64_t freq,
- uint32_t *dot_clock_p,
- uint32_t *fb_div_p,
- uint32_t *frac_fb_div_p,
- uint32_t *ref_div_p,
- uint32_t *post_div_p)
+void radeon_compute_pll(struct radeon_pll *pll,
+ uint64_t freq,
+ uint32_t *dot_clock_p,
+ uint32_t *fb_div_p,
+ uint32_t *frac_fb_div_p,
+ uint32_t *ref_div_p,
+ uint32_t *post_div_p)
{
uint32_t min_ref_div = pll->min_ref_div;
uint32_t max_ref_div = pll->max_ref_div;
@@ -513,7 +513,7 @@ static void radeon_compute_pll_legacy(struct radeon_pll *pll,
max_fractional_feed_div = pll->max_frac_feedback_div;
}
- for (post_div = min_post_div; post_div <= max_post_div; ++post_div) {
+ for (post_div = max_post_div; post_div >= min_post_div; --post_div) {
uint32_t ref_div;
if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
@@ -631,214 +631,6 @@ static void radeon_compute_pll_legacy(struct radeon_pll *pll,
*post_div_p = best_post_div;
}
-static bool
-calc_fb_div(struct radeon_pll *pll,
- uint32_t freq,
- uint32_t post_div,
- uint32_t ref_div,
- uint32_t *fb_div,
- uint32_t *fb_div_frac)
-{
- fixed20_12 feedback_divider, a, b;
- u32 vco_freq;
-
- vco_freq = freq * post_div;
- /* feedback_divider = vco_freq * ref_div / pll->reference_freq; */
- a.full = dfixed_const(pll->reference_freq);
- feedback_divider.full = dfixed_const(vco_freq);
- feedback_divider.full = dfixed_div(feedback_divider, a);
- a.full = dfixed_const(ref_div);
- feedback_divider.full = dfixed_mul(feedback_divider, a);
-
- if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
- /* feedback_divider = floor((feedback_divider * 10.0) + 0.5) * 0.1; */
- a.full = dfixed_const(10);
- feedback_divider.full = dfixed_mul(feedback_divider, a);
- feedback_divider.full += dfixed_const_half(0);
- feedback_divider.full = dfixed_floor(feedback_divider);
- feedback_divider.full = dfixed_div(feedback_divider, a);
-
- /* *fb_div = floor(feedback_divider); */
- a.full = dfixed_floor(feedback_divider);
- *fb_div = dfixed_trunc(a);
- /* *fb_div_frac = fmod(feedback_divider, 1.0) * 10.0; */
- a.full = dfixed_const(10);
- b.full = dfixed_mul(feedback_divider, a);
-
- feedback_divider.full = dfixed_floor(feedback_divider);
- feedback_divider.full = dfixed_mul(feedback_divider, a);
- feedback_divider.full = b.full - feedback_divider.full;
- *fb_div_frac = dfixed_trunc(feedback_divider);
- } else {
- /* *fb_div = floor(feedback_divider + 0.5); */
- feedback_divider.full += dfixed_const_half(0);
- feedback_divider.full = dfixed_floor(feedback_divider);
-
- *fb_div = dfixed_trunc(feedback_divider);
- *fb_div_frac = 0;
- }
-
- if (((*fb_div) < pll->min_feedback_div) || ((*fb_div) > pll->max_feedback_div))
- return false;
- else
- return true;
-}
-
-static bool
-calc_fb_ref_div(struct radeon_pll *pll,
- uint32_t freq,
- uint32_t post_div,
- uint32_t *fb_div,
- uint32_t *fb_div_frac,
- uint32_t *ref_div)
-{
- fixed20_12 ffreq, max_error, error, pll_out, a;
- u32 vco;
- u32 pll_out_min, pll_out_max;
-
- if (pll->flags & RADEON_PLL_IS_LCD) {
- pll_out_min = pll->lcd_pll_out_min;
- pll_out_max = pll->lcd_pll_out_max;
- } else {
- pll_out_min = pll->pll_out_min;
- pll_out_max = pll->pll_out_max;
- }
-
- ffreq.full = dfixed_const(freq);
- /* max_error = ffreq * 0.0025; */
- a.full = dfixed_const(400);
- max_error.full = dfixed_div(ffreq, a);
-
- for ((*ref_div) = pll->min_ref_div; (*ref_div) < pll->max_ref_div; ++(*ref_div)) {
- if (calc_fb_div(pll, freq, post_div, (*ref_div), fb_div, fb_div_frac)) {
- vco = pll->reference_freq * (((*fb_div) * 10) + (*fb_div_frac));
- vco = vco / ((*ref_div) * 10);
-
- if ((vco < pll_out_min) || (vco > pll_out_max))
- continue;
-
- /* pll_out = vco / post_div; */
- a.full = dfixed_const(post_div);
- pll_out.full = dfixed_const(vco);
- pll_out.full = dfixed_div(pll_out, a);
-
- if (pll_out.full >= ffreq.full) {
- error.full = pll_out.full - ffreq.full;
- if (error.full <= max_error.full)
- return true;
- }
- }
- }
- return false;
-}
-
-static void radeon_compute_pll_new(struct radeon_pll *pll,
- uint64_t freq,
- uint32_t *dot_clock_p,
- uint32_t *fb_div_p,
- uint32_t *frac_fb_div_p,
- uint32_t *ref_div_p,
- uint32_t *post_div_p)
-{
- u32 fb_div = 0, fb_div_frac = 0, post_div = 0, ref_div = 0;
- u32 best_freq = 0, vco_frequency;
- u32 pll_out_min, pll_out_max;
-
- if (pll->flags & RADEON_PLL_IS_LCD) {
- pll_out_min = pll->lcd_pll_out_min;
- pll_out_max = pll->lcd_pll_out_max;
- } else {
- pll_out_min = pll->pll_out_min;
- pll_out_max = pll->pll_out_max;
- }
-
- /* freq = freq / 10; */
- do_div(freq, 10);
-
- if (pll->flags & RADEON_PLL_USE_POST_DIV) {
- post_div = pll->post_div;
- if ((post_div < pll->min_post_div) || (post_div > pll->max_post_div))
- goto done;
-
- vco_frequency = freq * post_div;
- if ((vco_frequency < pll_out_min) || (vco_frequency > pll_out_max))
- goto done;
-
- if (pll->flags & RADEON_PLL_USE_REF_DIV) {
- ref_div = pll->reference_div;
- if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div))
- goto done;
- if (!calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac))
- goto done;
- }
- } else {
- for (post_div = pll->max_post_div; post_div >= pll->min_post_div; --post_div) {
- if (pll->flags & RADEON_PLL_LEGACY) {
- if ((post_div == 5) ||
- (post_div == 7) ||
- (post_div == 9) ||
- (post_div == 10) ||
- (post_div == 11))
- continue;
- }
-
- if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
- continue;
-
- vco_frequency = freq * post_div;
- if ((vco_frequency < pll_out_min) || (vco_frequency > pll_out_max))
- continue;
- if (pll->flags & RADEON_PLL_USE_REF_DIV) {
- ref_div = pll->reference_div;
- if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div))
- goto done;
- if (calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac))
- break;
- } else {
- if (calc_fb_ref_div(pll, freq, post_div, &fb_div, &fb_div_frac, &ref_div))
- break;
- }
- }
- }
-
- best_freq = pll->reference_freq * 10 * fb_div;
- best_freq += pll->reference_freq * fb_div_frac;
- best_freq = best_freq / (ref_div * post_div);
-
-done:
- if (best_freq == 0)
- DRM_ERROR("Couldn't find valid PLL dividers\n");
-
- *dot_clock_p = best_freq / 10;
- *fb_div_p = fb_div;
- *frac_fb_div_p = fb_div_frac;
- *ref_div_p = ref_div;
- *post_div_p = post_div;
-
- DRM_DEBUG_KMS("%u %d.%d, %d, %d\n", *dot_clock_p, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
-}
-
-void radeon_compute_pll(struct radeon_pll *pll,
- uint64_t freq,
- uint32_t *dot_clock_p,
- uint32_t *fb_div_p,
- uint32_t *frac_fb_div_p,
- uint32_t *ref_div_p,
- uint32_t *post_div_p)
-{
- switch (pll->algo) {
- case PLL_ALGO_NEW:
- radeon_compute_pll_new(pll, freq, dot_clock_p, fb_div_p,
- frac_fb_div_p, ref_div_p, post_div_p);
- break;
- case PLL_ALGO_LEGACY:
- default:
- radeon_compute_pll_legacy(pll, freq, dot_clock_p, fb_div_p,
- frac_fb_div_p, ref_div_p, post_div_p);
- break;
- }
-}
-
static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb);
@@ -1002,6 +794,24 @@ static int radeon_modeset_create_props(struct radeon_device *rdev)
radeon_underscan_enum_list[i].name);
}
+ rdev->mode_info.underscan_hborder_property =
+ drm_property_create(rdev->ddev,
+ DRM_MODE_PROP_RANGE,
+ "underscan hborder", 2);
+ 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);
+ 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;
}
@@ -1159,8 +969,14 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
((radeon_encoder->underscan_type == UNDERSCAN_AUTO) &&
drm_detect_hdmi_monitor(radeon_connector->edid) &&
is_hdtv_mode(mode)))) {
- radeon_crtc->h_border = (mode->hdisplay >> 5) + 16;
- radeon_crtc->v_border = (mode->vdisplay >> 5) + 16;
+ if (radeon_encoder->underscan_hborder != 0)
+ radeon_crtc->h_border = radeon_encoder->underscan_hborder;
+ else
+ radeon_crtc->h_border = (mode->hdisplay >> 5) + 16;
+ if (radeon_encoder->underscan_vborder != 0)
+ radeon_crtc->v_border = radeon_encoder->underscan_vborder;
+ else
+ radeon_crtc->v_border = (mode->vdisplay >> 5) + 16;
radeon_crtc->rmx_type = RMX_FULL;
src_v = crtc->mode.vdisplay;
dst_v = crtc->mode.vdisplay - (radeon_crtc->v_border * 2);
@@ -1195,3 +1011,156 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
}
return true;
}
+
+/*
+ * Retrieve current video scanout position of crtc on a given gpu.
+ *
+ * \param rdev Device to query.
+ * \param crtc Crtc to query.
+ * \param *vpos Location where vertical scanout position should be stored.
+ * \param *hpos Location where horizontal scanout position should go.
+ *
+ * Returns vpos as a positive number while in active scanout area.
+ * Returns vpos as a negative number inside vblank, counting the number
+ * of scanlines to go until end of vblank, e.g., -1 means "one scanline
+ * until start of active scanout / end of vblank."
+ *
+ * \return Flags, or'ed together as follows:
+ *
+ * RADEON_SCANOUTPOS_VALID = Query successfull.
+ * RADEON_SCANOUTPOS_INVBL = Inside vblank.
+ * RADEON_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
+ * this flag means that returned position may be offset by a constant but
+ * unknown small number of scanlines wrt. real scanout position.
+ *
+ */
+int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, int *hpos)
+{
+ u32 stat_crtc = 0, vbl = 0, position = 0;
+ int vbl_start, vbl_end, vtotal, ret = 0;
+ bool in_vbl = true;
+
+ if (ASIC_IS_DCE4(rdev)) {
+ if (crtc == 0) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC0_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC0_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 1) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC1_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC1_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 2) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC2_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC2_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 3) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC3_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC3_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 4) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC4_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC4_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 5) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC5_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC5_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ } else if (ASIC_IS_AVIVO(rdev)) {
+ if (crtc == 0) {
+ vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
+ position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 1) {
+ vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
+ position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ } else {
+ /* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */
+ if (crtc == 0) {
+ /* Assume vbl_end == 0, get vbl_start from
+ * upper 16 bits.
+ */
+ vbl = (RREG32(RADEON_CRTC_V_TOTAL_DISP) &
+ RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
+ /* Only retrieve vpos from upper 16 bits, set hpos == 0. */
+ position = (RREG32(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
+ stat_crtc = RREG32(RADEON_CRTC_STATUS);
+ if (!(stat_crtc & 1))
+ in_vbl = false;
+
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 1) {
+ vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) &
+ RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
+ position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
+ stat_crtc = RREG32(RADEON_CRTC2_STATUS);
+ if (!(stat_crtc & 1))
+ in_vbl = false;
+
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ }
+
+ /* Decode into vertical and horizontal scanout position. */
+ *vpos = position & 0x1fff;
+ *hpos = (position >> 16) & 0x1fff;
+
+ /* Valid vblank area boundaries from gpu retrieved? */
+ if (vbl > 0) {
+ /* Yes: Decode. */
+ ret |= RADEON_SCANOUTPOS_ACCURATE;
+ vbl_start = vbl & 0x1fff;
+ vbl_end = (vbl >> 16) & 0x1fff;
+ }
+ else {
+ /* No: Fake something reasonable which gives at least ok results. */
+ vbl_start = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vdisplay;
+ vbl_end = 0;
+ }
+
+ /* Test scanout position against vblank region. */
+ if ((*vpos < vbl_start) && (*vpos >= vbl_end))
+ in_vbl = false;
+
+ /* Check if inside vblank area and apply corrective offsets:
+ * vpos will then be >=0 in video scanout area, but negative
+ * within vblank area, counting down the number of lines until
+ * start of scanout.
+ */
+
+ /* Inside "upper part" of vblank area? Apply corrective offset if so: */
+ if (in_vbl && (*vpos >= vbl_start)) {
+ vtotal = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vtotal;
+ *vpos = *vpos - vtotal;
+ }
+
+ /* Correct for shifted end of vbl at vbl_end. */
+ *vpos = *vpos - vbl_end;
+
+ /* In vblank? */
+ if (in_vbl)
+ ret |= RADEON_SCANOUTPOS_INVBL;
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 29c1237c2e7b..88e4ea925900 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -47,9 +47,10 @@
* - 2.4.0 - add crtc id query
* - 2.5.0 - add get accel 2 to work around ddx breakage for evergreen
* - 2.6.0 - add tiling config query (r6xx+), add initial HiZ support (r300->r500)
+ * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 6
+#define KMS_DRIVER_MINOR 7
#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);
@@ -93,7 +94,6 @@ int radeon_benchmarking = 0;
int radeon_testing = 0;
int radeon_connector_table = 0;
int radeon_tv = 1;
-int radeon_new_pll = -1;
int radeon_audio = 1;
int radeon_disp_priority = 0;
int radeon_hw_i2c = 0;
@@ -131,9 +131,6 @@ module_param_named(connector_table, radeon_connector_table, int, 0444);
MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
module_param_named(tv, radeon_tv, int, 0444);
-MODULE_PARM_DESC(new_pll, "Select new PLL code");
-module_param_named(new_pll, radeon_new_pll, int, 0444);
-
MODULE_PARM_DESC(audio, "Audio enable (0 = disable)");
module_param_named(audio, radeon_audio, int, 0444);
@@ -203,8 +200,6 @@ static struct drm_driver driver_old = {
.irq_uninstall = radeon_driver_irq_uninstall,
.irq_handler = radeon_driver_irq_handler,
.reclaim_buffers = drm_core_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = radeon_ioctls,
.dma_ioctl = radeon_cp_buffers,
.fops = {
@@ -291,8 +286,6 @@ static struct drm_driver kms_driver = {
.irq_uninstall = radeon_driver_irq_uninstall_kms,
.irq_handler = radeon_driver_irq_handler_kms,
.reclaim_buffers = drm_core_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = radeon_ioctls_kms,
.gem_init_object = radeon_gem_object_init,
.gem_free_object = radeon_gem_object_free,
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index 2c293e8304d6..ae58b6849a2e 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -529,9 +529,9 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- if (dig->lvds_misc & ATOM_PANEL_MISC_DUAL)
+ if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL;
- if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
+ if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
args.v1.ucMisc |= (1 << 1);
} else {
if (dig->linkb)
@@ -558,18 +558,18 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
args.v2.ucTemporal = 0;
args.v2.ucFRC = 0;
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- if (dig->lvds_misc & ATOM_PANEL_MISC_DUAL)
+ if (dig->lcd_misc & ATOM_PANEL_MISC_DUAL)
args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
- if (dig->lvds_misc & ATOM_PANEL_MISC_SPATIAL) {
+ if (dig->lcd_misc & ATOM_PANEL_MISC_SPATIAL) {
args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN;
- if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
+ if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH;
}
- if (dig->lvds_misc & ATOM_PANEL_MISC_TEMPORAL) {
+ if (dig->lcd_misc & ATOM_PANEL_MISC_TEMPORAL) {
args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN;
- if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
+ if (dig->lcd_misc & ATOM_PANEL_MISC_888RGB)
args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH;
- if (((dig->lvds_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2)
+ if (((dig->lcd_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2)
args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4;
}
} else {
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 40b0c087b592..efa211898fe6 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -59,6 +59,8 @@ static struct fb_ops radeonfb_ops = {
.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,
};
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index b1f9a81b5d1d..216392d0353b 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -72,7 +72,15 @@ static bool radeon_fence_poll_locked(struct radeon_device *rdev)
bool wake = false;
unsigned long cjiffies;
- seq = RREG32(rdev->fence_drv.scratch_reg);
+ if (rdev->wb.enabled) {
+ u32 scratch_index;
+ if (rdev->wb.use_event)
+ scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
+ else
+ scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
+ seq = rdev->wb.wb[scratch_index/4];
+ } else
+ seq = RREG32(rdev->fence_drv.scratch_reg);
if (seq != rdev->fence_drv.last_seq) {
rdev->fence_drv.last_seq = seq;
rdev->fence_drv.last_jiffies = jiffies;
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 305049afde15..ace2e6384d40 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -348,10 +348,25 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
+ return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0);
+}
+
+int radeon_crtc_set_base_atomic(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, enum mode_set_atomic state)
+{
+ return radeon_crtc_do_set_base(crtc, fb, x, y, 1);
+}
+
+int radeon_crtc_do_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, int atomic)
+{
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct radeon_framebuffer *radeon_fb;
+ struct drm_framebuffer *target_fb;
struct drm_gem_object *obj;
struct radeon_bo *rbo;
uint64_t base;
@@ -364,14 +379,21 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
DRM_DEBUG_KMS("\n");
/* no fb bound */
- if (!crtc->fb) {
+ if (!atomic && !crtc->fb) {
DRM_DEBUG_KMS("No FB bound\n");
return 0;
}
- radeon_fb = to_radeon_framebuffer(crtc->fb);
+ if (atomic) {
+ radeon_fb = to_radeon_framebuffer(fb);
+ target_fb = fb;
+ }
+ else {
+ radeon_fb = to_radeon_framebuffer(crtc->fb);
+ target_fb = crtc->fb;
+ }
- switch (crtc->fb->bits_per_pixel) {
+ switch (target_fb->bits_per_pixel) {
case 8:
format = 2;
break;
@@ -415,10 +437,10 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
crtc_offset_cntl = 0;
- pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8);
- crtc_pitch = (((pitch_pixels * crtc->fb->bits_per_pixel) +
- ((crtc->fb->bits_per_pixel * 8) - 1)) /
- (crtc->fb->bits_per_pixel * 8));
+ pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8);
+ crtc_pitch = (((pitch_pixels * target_fb->bits_per_pixel) +
+ ((target_fb->bits_per_pixel * 8) - 1)) /
+ (target_fb->bits_per_pixel * 8));
crtc_pitch |= crtc_pitch << 16;
@@ -443,14 +465,14 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
crtc_tile_x0_y0 = x | (y << 16);
base &= ~0x7ff;
} else {
- int byteshift = crtc->fb->bits_per_pixel >> 4;
+ int byteshift = target_fb->bits_per_pixel >> 4;
int tile_addr = (((y >> 3) * pitch_pixels + x) >> (8 - byteshift)) << 11;
base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8);
crtc_offset_cntl |= (y % 16);
}
} else {
int offset = y * pitch_pixels + x;
- switch (crtc->fb->bits_per_pixel) {
+ switch (target_fb->bits_per_pixel) {
case 8:
offset *= 1;
break;
@@ -496,8 +518,8 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset);
WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch);
- if (old_fb && old_fb != crtc->fb) {
- radeon_fb = to_radeon_framebuffer(old_fb);
+ if (!atomic && fb && fb != crtc->fb) {
+ radeon_fb = to_radeon_framebuffer(fb);
rbo = radeon_fb->obj->driver_private;
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
@@ -717,10 +739,6 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
pll = &rdev->clock.p1pll;
pll->flags = RADEON_PLL_LEGACY;
- if (radeon_new_pll == 1)
- pll->algo = PLL_ALGO_NEW;
- else
- pll->algo = PLL_ALGO_LEGACY;
if (mode->clock > 200000) /* range limits??? */
pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
@@ -1040,6 +1058,7 @@ static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
.mode_fixup = radeon_crtc_mode_fixup,
.mode_set = radeon_crtc_mode_set,
.mode_set_base = radeon_crtc_set_base,
+ .mode_set_base_atomic = radeon_crtc_set_base_atomic,
.prepare = radeon_crtc_prepare,
.commit = radeon_crtc_commit,
.load_lut = radeon_crtc_load_lut,
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 454c1dc7ea45..92457163d070 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -35,6 +35,7 @@
#include <drm_edid.h>
#include <drm_dp_helper.h>
#include <drm_fixed.h>
+#include <drm_crtc_helper.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
@@ -149,12 +150,6 @@ struct radeon_tmds_pll {
#define RADEON_PLL_USE_POST_DIV (1 << 12)
#define RADEON_PLL_IS_LCD (1 << 13)
-/* pll algo */
-enum radeon_pll_algo {
- PLL_ALGO_LEGACY,
- PLL_ALGO_NEW
-};
-
struct radeon_pll {
/* reference frequency */
uint32_t reference_freq;
@@ -187,8 +182,6 @@ struct radeon_pll {
/* pll id */
uint32_t id;
- /* pll algo */
- enum radeon_pll_algo algo;
};
struct radeon_i2c_chan {
@@ -240,6 +233,8 @@ struct radeon_mode_info {
struct drm_property *tmds_pll_property;
/* underscan */
struct drm_property *underscan_property;
+ struct drm_property *underscan_hborder_property;
+ struct drm_property *underscan_vborder_property;
/* hardcoded DFP edid from BIOS */
struct edid *bios_hardcoded_edid;
@@ -335,22 +330,24 @@ struct radeon_encoder_ext_tmds {
struct radeon_atom_ss {
uint16_t percentage;
uint8_t type;
- uint8_t step;
+ uint16_t step;
uint8_t delay;
uint8_t range;
uint8_t refdiv;
+ /* asic_ss */
+ uint16_t rate;
+ uint16_t amount;
};
struct radeon_encoder_atom_dig {
bool linkb;
/* atom dig */
bool coherent_mode;
- int dig_encoder; /* -1 disabled, 0 DIGA, 1 DIGB */
- /* atom lvds */
- uint32_t lvds_misc;
+ int dig_encoder; /* -1 disabled, 0 DIGA, 1 DIGB, etc. */
+ /* atom lvds/edp */
+ uint32_t lcd_misc;
uint16_t panel_pwr_delay;
- enum radeon_pll_algo pll_algo;
- struct radeon_atom_ss *ss;
+ uint32_t lcd_ss_id;
/* panel mode */
struct drm_display_mode native_mode;
};
@@ -369,6 +366,8 @@ struct radeon_encoder {
uint32_t pixel_clock;
enum radeon_rmx_type rmx_type;
enum radeon_underscan_type underscan_type;
+ uint32_t underscan_hborder;
+ uint32_t underscan_vborder;
struct drm_display_mode native_mode;
void *enc_priv;
int audio_polling_active;
@@ -435,6 +434,11 @@ struct radeon_framebuffer {
struct drm_gem_object *obj;
};
+/* radeon_get_crtc_scanoutpos() return flags */
+#define RADEON_SCANOUTPOS_VALID (1 << 0)
+#define RADEON_SCANOUTPOS_INVBL (1 << 1)
+#define RADEON_SCANOUTPOS_ACCURATE (1 << 2)
+
extern enum radeon_tv_std
radeon_combios_get_tv_info(struct radeon_device *rdev);
extern enum radeon_tv_std
@@ -490,6 +494,13 @@ extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector);
+extern bool radeon_atombios_get_ppll_ss_info(struct radeon_device *rdev,
+ struct radeon_atom_ss *ss,
+ int id);
+extern bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
+ struct radeon_atom_ss *ss,
+ int id, u32 clock);
+
extern void radeon_compute_pll(struct radeon_pll *pll,
uint64_t freq,
uint32_t *dot_clock_p,
@@ -513,6 +524,10 @@ extern void radeon_encoder_set_active_device(struct drm_encoder *encoder);
extern void radeon_crtc_load_lut(struct drm_crtc *crtc);
extern int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb);
+extern int atombios_crtc_set_base_atomic(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y,
+ enum mode_set_atomic state);
extern int atombios_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
@@ -522,7 +537,13 @@ extern void atombios_crtc_dpms(struct drm_crtc *crtc, int mode);
extern int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb);
-
+extern int radeon_crtc_set_base_atomic(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y,
+ enum mode_set_atomic state);
+extern int radeon_crtc_do_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y, int atomic);
extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
struct drm_file *file_priv,
uint32_t handle,
@@ -531,6 +552,8 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
int x, int y);
+extern int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, int *hpos);
+
extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
extern struct edid *
radeon_combios_get_hardcoded_edid(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index b3b5306bb578..d7ab91416410 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -435,7 +435,7 @@ int radeon_bo_get_surface_reg(struct radeon_bo *bo)
out:
radeon_set_surface_reg(rdev, i, bo->tiling_flags, bo->pitch,
- bo->tbo.mem.mm_node->start << PAGE_SHIFT,
+ bo->tbo.mem.start << PAGE_SHIFT,
bo->tbo.num_pages << PAGE_SHIFT);
return 0;
}
@@ -532,7 +532,7 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
rdev = rbo->rdev;
if (bo->mem.mem_type == TTM_PL_VRAM) {
size = bo->mem.num_pages << PAGE_SHIFT;
- offset = bo->mem.mm_node->start << PAGE_SHIFT;
+ offset = bo->mem.start << PAGE_SHIFT;
if ((offset + size) > rdev->mc.visible_vram_size) {
/* hurrah the memory is not visible ! */
radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM);
@@ -540,7 +540,7 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
r = ttm_bo_validate(bo, &rbo->placement, false, true, false);
if (unlikely(r != 0))
return r;
- offset = bo->mem.mm_node->start << PAGE_SHIFT;
+ offset = bo->mem.start << PAGE_SHIFT;
/* this should not happen */
if ((offset + size) > rdev->mc.visible_vram_size)
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index f87efec76236..8c9b2ef32c68 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -712,73 +712,21 @@ void radeon_pm_compute_clocks(struct radeon_device *rdev)
static bool radeon_pm_in_vbl(struct radeon_device *rdev)
{
- u32 stat_crtc = 0, vbl = 0, position = 0;
+ int crtc, vpos, hpos, vbl_status;
bool in_vbl = true;
- if (ASIC_IS_DCE4(rdev)) {
- if (rdev->pm.active_crtcs & (1 << 0)) {
- vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
- EVERGREEN_CRTC0_REGISTER_OFFSET) & 0xfff;
- position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
- EVERGREEN_CRTC0_REGISTER_OFFSET) & 0xfff;
- }
- if (rdev->pm.active_crtcs & (1 << 1)) {
- vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
- EVERGREEN_CRTC1_REGISTER_OFFSET) & 0xfff;
- position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
- EVERGREEN_CRTC1_REGISTER_OFFSET) & 0xfff;
- }
- if (rdev->pm.active_crtcs & (1 << 2)) {
- vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
- EVERGREEN_CRTC2_REGISTER_OFFSET) & 0xfff;
- position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
- EVERGREEN_CRTC2_REGISTER_OFFSET) & 0xfff;
- }
- if (rdev->pm.active_crtcs & (1 << 3)) {
- vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
- EVERGREEN_CRTC3_REGISTER_OFFSET) & 0xfff;
- position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
- EVERGREEN_CRTC3_REGISTER_OFFSET) & 0xfff;
- }
- if (rdev->pm.active_crtcs & (1 << 4)) {
- vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
- EVERGREEN_CRTC4_REGISTER_OFFSET) & 0xfff;
- position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
- EVERGREEN_CRTC4_REGISTER_OFFSET) & 0xfff;
- }
- if (rdev->pm.active_crtcs & (1 << 5)) {
- vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
- EVERGREEN_CRTC5_REGISTER_OFFSET) & 0xfff;
- position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
- EVERGREEN_CRTC5_REGISTER_OFFSET) & 0xfff;
- }
- } else if (ASIC_IS_AVIVO(rdev)) {
- if (rdev->pm.active_crtcs & (1 << 0)) {
- vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END) & 0xfff;
- position = RREG32(AVIVO_D1CRTC_STATUS_POSITION) & 0xfff;
- }
- if (rdev->pm.active_crtcs & (1 << 1)) {
- vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END) & 0xfff;
- position = RREG32(AVIVO_D2CRTC_STATUS_POSITION) & 0xfff;
- }
- if (position < vbl && position > 1)
- in_vbl = false;
- } else {
- if (rdev->pm.active_crtcs & (1 << 0)) {
- stat_crtc = RREG32(RADEON_CRTC_STATUS);
- if (!(stat_crtc & 1))
- in_vbl = false;
- }
- if (rdev->pm.active_crtcs & (1 << 1)) {
- stat_crtc = RREG32(RADEON_CRTC2_STATUS);
- if (!(stat_crtc & 1))
+ /* Iterate over all active crtc's. All crtc's must be in vblank,
+ * otherwise return in_vbl == false.
+ */
+ for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
+ if (rdev->pm.active_crtcs & (1 << crtc)) {
+ vbl_status = radeon_get_crtc_scanoutpos(rdev, crtc, &vpos, &hpos);
+ if ((vbl_status & RADEON_SCANOUTPOS_VALID) &&
+ !(vbl_status & RADEON_SCANOUTPOS_INVBL))
in_vbl = false;
}
}
- if (position < vbl && position > 1)
- in_vbl = false;
-
return in_vbl;
}
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 261e98a276db..6ea798ce8218 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -247,10 +247,14 @@ void radeon_ib_pool_fini(struct radeon_device *rdev)
*/
void radeon_ring_free_size(struct radeon_device *rdev)
{
- if (rdev->family >= CHIP_R600)
- rdev->cp.rptr = RREG32(R600_CP_RB_RPTR);
- else
- rdev->cp.rptr = RREG32(RADEON_CP_RB_RPTR);
+ if (rdev->wb.enabled)
+ rdev->cp.rptr = rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4];
+ else {
+ if (rdev->family >= CHIP_R600)
+ rdev->cp.rptr = RREG32(R600_CP_RB_RPTR);
+ else
+ rdev->cp.rptr = RREG32(RADEON_CP_RB_RPTR);
+ }
/* This works because ring_size is a power of 2 */
rdev->cp.ring_free_dw = (rdev->cp.rptr + (rdev->cp.ring_size / 4));
rdev->cp.ring_free_dw -= rdev->cp.wptr;
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index a823d8fe54c2..fe95bb35317e 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -152,6 +152,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_TT:
+ man->func = &ttm_bo_manager_func;
man->gpu_offset = rdev->mc.gtt_start;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
@@ -173,6 +174,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
break;
case TTM_PL_VRAM:
/* "On-card" video ram */
+ man->func = &ttm_bo_manager_func;
man->gpu_offset = rdev->mc.vram_start;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_MAPPABLE;
@@ -246,8 +248,8 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
if (unlikely(r)) {
return r;
}
- old_start = old_mem->mm_node->start << PAGE_SHIFT;
- new_start = new_mem->mm_node->start << PAGE_SHIFT;
+ old_start = old_mem->start << PAGE_SHIFT;
+ new_start = new_mem->start << PAGE_SHIFT;
switch (old_mem->mem_type) {
case TTM_PL_VRAM:
@@ -326,14 +328,7 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
}
r = ttm_bo_move_ttm(bo, true, no_wait_reserve, no_wait_gpu, new_mem);
out_cleanup:
- if (tmp_mem.mm_node) {
- struct ttm_bo_global *glob = rdev->mman.bdev.glob;
-
- spin_lock(&glob->lru_lock);
- drm_mm_put_block(tmp_mem.mm_node);
- spin_unlock(&glob->lru_lock);
- return r;
- }
+ ttm_bo_mem_put(bo, &tmp_mem);
return r;
}
@@ -372,14 +367,7 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo,
goto out_cleanup;
}
out_cleanup:
- if (tmp_mem.mm_node) {
- struct ttm_bo_global *glob = rdev->mman.bdev.glob;
-
- spin_lock(&glob->lru_lock);
- drm_mm_put_block(tmp_mem.mm_node);
- spin_unlock(&glob->lru_lock);
- return r;
- }
+ ttm_bo_mem_put(bo, &tmp_mem);
return r;
}
@@ -449,14 +437,14 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_
#if __OS_HAS_AGP
if (rdev->flags & RADEON_IS_AGP) {
/* RADEON_IS_AGP is set only if AGP is active */
- mem->bus.offset = mem->mm_node->start << PAGE_SHIFT;
+ mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = rdev->mc.agp_base;
mem->bus.is_iomem = !rdev->ddev->agp->cant_use_aperture;
}
#endif
break;
case TTM_PL_VRAM:
- mem->bus.offset = mem->mm_node->start << PAGE_SHIFT;
+ mem->bus.offset = mem->start << PAGE_SHIFT;
/* check if it's visible */
if ((mem->bus.offset + mem->bus.size) > rdev->mc.visible_vram_size)
return -EINVAL;
@@ -699,7 +687,7 @@ static int radeon_ttm_backend_bind(struct ttm_backend *backend,
int r;
gtt = container_of(backend, struct radeon_ttm_backend, backend);
- gtt->offset = bo_mem->mm_node->start << PAGE_SHIFT;
+ gtt->offset = bo_mem->start << PAGE_SHIFT;
if (!gtt->num_pages) {
WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", gtt->num_pages, bo_mem, backend);
}
@@ -798,9 +786,9 @@ static int radeon_ttm_debugfs_init(struct radeon_device *rdev)
radeon_mem_types_list[i].show = &radeon_mm_dump_table;
radeon_mem_types_list[i].driver_features = 0;
if (i == 0)
- radeon_mem_types_list[i].data = &rdev->mman.bdev.man[TTM_PL_VRAM].manager;
+ radeon_mem_types_list[i].data = &rdev->mman.bdev.man[TTM_PL_VRAM].priv;
else
- radeon_mem_types_list[i].data = &rdev->mman.bdev.man[TTM_PL_TT].manager;
+ radeon_mem_types_list[i].data = &rdev->mman.bdev.man[TTM_PL_TT].priv;
}
/* Add ttm page pool to debugfs */
diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen
index f78fd592544d..ac40fd39d787 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/evergreen
+++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen
@@ -22,6 +22,10 @@ evergreen 0x9400
0x00008B10 PA_SC_LINE_STIPPLE_STATE
0x00008BF0 PA_SC_ENHANCE
0x00008D8C SQ_DYN_GPR_CNTL_PS_FLUSH_REQ
+0x00008D90 SQ_DYN_GPR_OPTIMIZATION
+0x00008D94 SQ_DYN_GPR_SIMD_LOCK_EN
+0x00008D98 SQ_DYN_GPR_THREAD_LIMIT
+0x00008D9C SQ_DYN_GPR_LDS_LIMIT
0x00008C00 SQ_CONFIG
0x00008C04 SQ_GPR_RESOURCE_MGMT_1
0x00008C08 SQ_GPR_RESOURCE_MGMT_2
@@ -34,6 +38,10 @@ evergreen 0x9400
0x00008C24 SQ_STACK_RESOURCE_MGMT_2
0x00008C28 SQ_STACK_RESOURCE_MGMT_3
0x00008DF8 SQ_CONST_MEM_BASE
+0x00008E20 SQ_STATIC_THREAD_MGMT_1
+0x00008E24 SQ_STATIC_THREAD_MGMT_2
+0x00008E28 SQ_STATIC_THREAD_MGMT_3
+0x00008E2C SQ_LDS_RESOURCE_MGMT
0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS
0x00009100 SPI_CONFIG_CNTL
0x0000913C SPI_CONFIG_CNTL_1
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index ae2b76b9a388..f683e51a2a06 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -397,6 +397,12 @@ static int rs400_startup(struct radeon_device *rdev)
r = rs400_gart_enable(rdev);
if (r)
return r;
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
r100_irq_set(rdev);
rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -406,9 +412,6 @@ static int rs400_startup(struct radeon_device *rdev)
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
return r;
}
- r = r100_wb_init(rdev);
- if (r)
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -443,7 +446,7 @@ int rs400_resume(struct radeon_device *rdev)
int rs400_suspend(struct radeon_device *rdev)
{
r100_cp_disable(rdev);
- r100_wb_disable(rdev);
+ radeon_wb_disable(rdev);
r100_irq_disable(rdev);
rs400_gart_disable(rdev);
return 0;
@@ -452,7 +455,7 @@ int rs400_suspend(struct radeon_device *rdev)
void rs400_fini(struct radeon_device *rdev)
{
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_gem_fini(rdev);
rs400_gart_fini(rdev);
@@ -526,7 +529,7 @@ int rs400_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
rs400_gart_fini(rdev);
radeon_irq_kms_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 51d5f7b5ab21..b091a1f6fa4e 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -796,6 +796,12 @@ static int rs600_startup(struct radeon_device *rdev)
r = rs600_gart_enable(rdev);
if (r)
return r;
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
rs600_irq_set(rdev);
rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -805,9 +811,6 @@ static int rs600_startup(struct radeon_device *rdev)
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
return r;
}
- r = r100_wb_init(rdev);
- if (r)
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -848,7 +851,7 @@ int rs600_suspend(struct radeon_device *rdev)
{
r600_audio_fini(rdev);
r100_cp_disable(rdev);
- r100_wb_disable(rdev);
+ radeon_wb_disable(rdev);
rs600_irq_disable(rdev);
rs600_gart_disable(rdev);
return 0;
@@ -858,7 +861,7 @@ void rs600_fini(struct radeon_device *rdev)
{
r600_audio_fini(rdev);
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_gem_fini(rdev);
rs600_gart_fini(rdev);
@@ -932,7 +935,7 @@ int rs600_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
rs600_gart_fini(rdev);
radeon_irq_kms_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index 4dc2a87ea680..0137d3e3728d 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -616,6 +616,12 @@ static int rs690_startup(struct radeon_device *rdev)
r = rs400_gart_enable(rdev);
if (r)
return r;
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
rs600_irq_set(rdev);
rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -625,9 +631,6 @@ static int rs690_startup(struct radeon_device *rdev)
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
return r;
}
- r = r100_wb_init(rdev);
- if (r)
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -668,7 +671,7 @@ int rs690_suspend(struct radeon_device *rdev)
{
r600_audio_fini(rdev);
r100_cp_disable(rdev);
- r100_wb_disable(rdev);
+ radeon_wb_disable(rdev);
rs600_irq_disable(rdev);
rs400_gart_disable(rdev);
return 0;
@@ -678,7 +681,7 @@ void rs690_fini(struct radeon_device *rdev)
{
r600_audio_fini(rdev);
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_gem_fini(rdev);
rs400_gart_fini(rdev);
@@ -753,7 +756,7 @@ int rs690_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
rs400_gart_fini(rdev);
radeon_irq_kms_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index 4d6e86041a9f..5d569f41f4ae 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -386,6 +386,12 @@ static int rv515_startup(struct radeon_device *rdev)
if (r)
return r;
}
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
rs600_irq_set(rdev);
rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
@@ -395,9 +401,6 @@ static int rv515_startup(struct radeon_device *rdev)
dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
return r;
}
- r = r100_wb_init(rdev);
- if (r)
- dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
r = r100_ib_init(rdev);
if (r) {
dev_err(rdev->dev, "failled initializing IB (%d).\n", r);
@@ -431,7 +434,7 @@ int rv515_resume(struct radeon_device *rdev)
int rv515_suspend(struct radeon_device *rdev)
{
r100_cp_disable(rdev);
- r100_wb_disable(rdev);
+ radeon_wb_disable(rdev);
rs600_irq_disable(rdev);
if (rdev->flags & RADEON_IS_PCIE)
rv370_pcie_gart_disable(rdev);
@@ -447,7 +450,7 @@ void rv515_set_safe_registers(struct radeon_device *rdev)
void rv515_fini(struct radeon_device *rdev)
{
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_gem_fini(rdev);
rv370_pcie_gart_fini(rdev);
@@ -527,7 +530,7 @@ int rv515_init(struct radeon_device *rdev)
/* Somethings want wront with the accel init stop accel */
dev_err(rdev->dev, "Disabling GPU acceleration\n");
r100_cp_fini(rdev);
- r100_wb_fini(rdev);
+ radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_irq_kms_fini(rdev);
rv370_pcie_gart_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 9490da700749..245374e2b778 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -269,6 +269,7 @@ void r700_cp_stop(struct radeon_device *rdev)
{
rdev->mc.active_vram_size = rdev->mc.visible_vram_size;
WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT));
+ WREG32(SCRATCH_UMSK, 0);
}
static int rv770_cp_load_microcode(struct radeon_device *rdev)
@@ -643,10 +644,11 @@ static void rv770_gpu_init(struct radeon_device *rdev)
else
gb_tiling_config |= BANK_TILING((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT);
rdev->config.rv770.tiling_nbanks = 4 << ((gb_tiling_config >> 4) & 0x3);
-
- gb_tiling_config |= GROUP_SIZE(0);
- rdev->config.rv770.tiling_group_size = 256;
-
+ gb_tiling_config |= GROUP_SIZE((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT);
+ if ((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT)
+ rdev->config.rv770.tiling_group_size = 512;
+ else
+ rdev->config.rv770.tiling_group_size = 256;
if (((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT) > 3) {
gb_tiling_config |= ROW_TILING(3);
gb_tiling_config |= SAMPLE_SPLIT(3);
@@ -1030,19 +1032,12 @@ static int rv770_startup(struct radeon_device *rdev)
rdev->asic->copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
- /* pin copy shader into vram */
- if (rdev->r600_blit.shader_obj) {
- r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
- if (unlikely(r != 0))
- return r;
- r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
- &rdev->r600_blit.shader_gpu_addr);
- radeon_bo_unreserve(rdev->r600_blit.shader_obj);
- if (r) {
- DRM_ERROR("failed to pin blit object %d\n", r);
- return r;
- }
- }
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
/* Enable IRQ */
r = r600_irq_init(rdev);
if (r) {
@@ -1061,8 +1056,7 @@ static int rv770_startup(struct radeon_device *rdev)
r = r600_cp_resume(rdev);
if (r)
return r;
- /* write back buffer are not vital so don't worry about failure */
- r600_wb_enable(rdev);
+
return 0;
}
@@ -1108,7 +1102,7 @@ int rv770_suspend(struct radeon_device *rdev)
r700_cp_stop(rdev);
rdev->cp.ready = false;
r600_irq_suspend(rdev);
- r600_wb_disable(rdev);
+ radeon_wb_disable(rdev);
rv770_pcie_gart_disable(rdev);
/* unpin shaders bo */
if (rdev->r600_blit.shader_obj) {
@@ -1203,8 +1197,8 @@ int rv770_init(struct radeon_device *rdev)
if (r) {
dev_err(rdev->dev, "disabling GPU acceleration\n");
r700_cp_fini(rdev);
- r600_wb_fini(rdev);
r600_irq_fini(rdev);
+ radeon_wb_fini(rdev);
radeon_irq_kms_fini(rdev);
rv770_pcie_gart_fini(rdev);
rdev->accel_working = false;
@@ -1236,8 +1230,8 @@ void rv770_fini(struct radeon_device *rdev)
{
r600_blit_fini(rdev);
r700_cp_fini(rdev);
- r600_wb_fini(rdev);
r600_irq_fini(rdev);
+ radeon_wb_fini(rdev);
radeon_irq_kms_fini(rdev);
rv770_pcie_gart_fini(rdev);
rv770_vram_scratch_fini(rdev);
diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
index 2a2830f5a840..fa64d25d4248 100644
--- a/drivers/gpu/drm/savage/savage_drv.c
+++ b/drivers/gpu/drm/savage/savage_drv.c
@@ -42,8 +42,6 @@ static struct drm_driver driver = {
.lastclose = savage_driver_lastclose,
.unload = savage_driver_unload,
.reclaim_buffers = savage_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = savage_ioctls,
.dma_ioctl = savage_bci_buffers,
.fops = {
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
index 4bb10ef6676a..4caf5d01cfd3 100644
--- a/drivers/gpu/drm/sis/sis_drv.c
+++ b/drivers/gpu/drm/sis/sis_drv.c
@@ -67,13 +67,10 @@ static struct drm_driver driver = {
.driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR,
.load = sis_driver_load,
.unload = sis_driver_unload,
- .context_dtor = NULL,
.dma_quiescent = sis_idle,
.reclaim_buffers = NULL,
.reclaim_buffers_idlelocked = sis_reclaim_buffers_locked,
.lastclose = sis_lastclose,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = sis_ioctls,
.fops = {
.owner = THIS_MODULE,
diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
index 640567ef713d..b70fa91d761a 100644
--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
+++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
@@ -42,8 +42,6 @@ static struct pci_device_id pciidlist[] = {
static struct drm_driver driver = {
.driver_features = DRIVER_USE_MTRR,
.reclaim_buffers = drm_core_reclaim_buffers,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.fops = {
.owner = THIS_MODULE,
.open = drm_open,
diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile
index b256d4adfafe..f3cf6f02c997 100644
--- a/drivers/gpu/drm/ttm/Makefile
+++ b/drivers/gpu/drm/ttm/Makefile
@@ -4,6 +4,7 @@
ccflags-y := -Iinclude/drm
ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \
ttm_bo_util.o ttm_bo_vm.o ttm_module.o \
- ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o
+ ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \
+ ttm_bo_manager.o
obj-$(CONFIG_DRM_TTM) += ttm.o
diff --git a/drivers/gpu/drm/ttm/ttm_agp_backend.c b/drivers/gpu/drm/ttm/ttm_agp_backend.c
index 4bf69c404491..f999e36f30b4 100644
--- a/drivers/gpu/drm/ttm/ttm_agp_backend.c
+++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c
@@ -74,6 +74,7 @@ static int ttm_agp_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem)
{
struct ttm_agp_backend *agp_be =
container_of(backend, struct ttm_agp_backend, backend);
+ struct drm_mm_node *node = bo_mem->mm_node;
struct agp_memory *mem = agp_be->mem;
int cached = (bo_mem->placement & TTM_PL_FLAG_CACHED);
int ret;
@@ -81,7 +82,7 @@ static int ttm_agp_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem)
mem->is_flushed = 1;
mem->type = (cached) ? AGP_USER_CACHED_MEMORY : AGP_USER_MEMORY;
- ret = agp_bind_memory(mem, bo_mem->mm_node->start);
+ ret = agp_bind_memory(mem, node->start);
if (ret)
printk(KERN_ERR TTM_PFX "AGP Bind memory failed.\n");
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index db809e034cc4..a1cb783c7131 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -84,11 +84,8 @@ static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type)
man->available_caching);
printk(KERN_ERR TTM_PFX " default_caching: 0x%08X\n",
man->default_caching);
- if (mem_type != TTM_PL_SYSTEM) {
- spin_lock(&bdev->glob->lru_lock);
- drm_mm_debug_table(&man->manager, TTM_PFX);
- spin_unlock(&bdev->glob->lru_lock);
- }
+ if (mem_type != TTM_PL_SYSTEM)
+ (*man->func->debug)(man, TTM_PFX);
}
static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
@@ -169,18 +166,13 @@ static void ttm_bo_release_list(struct kref *list_kref)
int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible)
{
-
if (interruptible) {
- int ret = 0;
-
- ret = wait_event_interruptible(bo->event_queue,
+ return wait_event_interruptible(bo->event_queue,
atomic_read(&bo->reserved) == 0);
- if (unlikely(ret != 0))
- return ret;
} else {
wait_event(bo->event_queue, atomic_read(&bo->reserved) == 0);
+ return 0;
}
- return 0;
}
EXPORT_SYMBOL(ttm_bo_wait_unreserved);
@@ -421,7 +413,7 @@ moved:
if (bo->mem.mm_node) {
spin_lock(&bo->lock);
- bo->offset = (bo->mem.mm_node->start << PAGE_SHIFT) +
+ bo->offset = (bo->mem.start << PAGE_SHIFT) +
bdev->man[bo->mem.mem_type].gpu_offset;
bo->cur_placement = bo->mem.placement;
spin_unlock(&bo->lock);
@@ -442,135 +434,144 @@ out_err:
}
/**
- * Call bo::reserved and with the lru lock held.
+ * Call bo::reserved.
* Will release GPU memory type usage on destruction.
- * This is the place to put in driver specific hooks.
- * Will release the bo::reserved lock and the
- * lru lock on exit.
+ * This is the place to put in driver specific hooks to release
+ * driver private resources.
+ * Will release the bo::reserved lock.
*/
static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo)
{
- struct ttm_bo_global *glob = bo->glob;
-
if (bo->ttm) {
-
- /**
- * Release the lru_lock, since we don't want to have
- * an atomic requirement on ttm_tt[unbind|destroy].
- */
-
- spin_unlock(&glob->lru_lock);
ttm_tt_unbind(bo->ttm);
ttm_tt_destroy(bo->ttm);
bo->ttm = NULL;
- spin_lock(&glob->lru_lock);
}
- if (bo->mem.mm_node) {
- drm_mm_put_block(bo->mem.mm_node);
- bo->mem.mm_node = NULL;
- }
+ ttm_bo_mem_put(bo, &bo->mem);
atomic_set(&bo->reserved, 0);
wake_up_all(&bo->event_queue);
- spin_unlock(&glob->lru_lock);
}
-
-/**
- * If bo idle, remove from delayed- and lru lists, and unref.
- * If not idle, and already on delayed list, do nothing.
- * If not idle, and not on delayed list, put on delayed list,
- * up the list_kref and schedule a delayed list check.
- */
-
-static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
+static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
{
struct ttm_bo_device *bdev = bo->bdev;
struct ttm_bo_global *glob = bo->glob;
- struct ttm_bo_driver *driver = bdev->driver;
+ struct ttm_bo_driver *driver;
+ void *sync_obj;
+ void *sync_obj_arg;
+ int put_count;
int ret;
spin_lock(&bo->lock);
-retry:
- (void) ttm_bo_wait(bo, false, false, !remove_all);
-
+ (void) ttm_bo_wait(bo, false, false, true);
if (!bo->sync_obj) {
- int put_count;
-
- spin_unlock(&bo->lock);
spin_lock(&glob->lru_lock);
- ret = ttm_bo_reserve_locked(bo, false, !remove_all, false, 0);
/**
- * Someone else has the object reserved. Bail and retry.
+ * Lock inversion between bo::reserve and bo::lock here,
+ * but that's OK, since we're only trylocking.
*/
- if (unlikely(ret == -EBUSY)) {
- spin_unlock(&glob->lru_lock);
- spin_lock(&bo->lock);
- goto requeue;
- }
-
- /**
- * We can re-check for sync object without taking
- * the bo::lock since setting the sync object requires
- * also bo::reserved. A busy object at this point may
- * be caused by another thread starting an accelerated
- * eviction.
- */
+ ret = ttm_bo_reserve_locked(bo, false, true, false, 0);
- if (unlikely(bo->sync_obj)) {
- atomic_set(&bo->reserved, 0);
- wake_up_all(&bo->event_queue);
- spin_unlock(&glob->lru_lock);
- spin_lock(&bo->lock);
- if (remove_all)
- goto retry;
- else
- goto requeue;
- }
+ if (unlikely(ret == -EBUSY))
+ goto queue;
+ spin_unlock(&bo->lock);
put_count = ttm_bo_del_from_lru(bo);
- if (!list_empty(&bo->ddestroy)) {
- list_del_init(&bo->ddestroy);
- ++put_count;
- }
-
+ spin_unlock(&glob->lru_lock);
ttm_bo_cleanup_memtype_use(bo);
while (put_count--)
kref_put(&bo->list_kref, ttm_bo_ref_bug);
- return 0;
+ return;
+ } else {
+ spin_lock(&glob->lru_lock);
}
-requeue:
+queue:
+ sync_obj = bo->sync_obj;
+ sync_obj_arg = bo->sync_obj_arg;
+ driver = bdev->driver;
+
+ kref_get(&bo->list_kref);
+ list_add_tail(&bo->ddestroy, &bdev->ddestroy);
+ spin_unlock(&glob->lru_lock);
+ spin_unlock(&bo->lock);
+
+ if (sync_obj)
+ driver->sync_obj_flush(sync_obj, sync_obj_arg);
+ schedule_delayed_work(&bdev->wq,
+ ((HZ / 100) < 1) ? 1 : HZ / 100);
+}
+
+/**
+ * function ttm_bo_cleanup_refs
+ * If bo idle, remove from delayed- and lru lists, and unref.
+ * If not idle, do nothing.
+ *
+ * @interruptible Any sleeps should occur interruptibly.
+ * @no_wait_reserve Never wait for reserve. Return -EBUSY instead.
+ * @no_wait_gpu Never wait for gpu. Return -EBUSY instead.
+ */
+
+static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,
+ bool interruptible,
+ bool no_wait_reserve,
+ bool no_wait_gpu)
+{
+ struct ttm_bo_global *glob = bo->glob;
+ int put_count;
+ int ret = 0;
+
+retry:
+ spin_lock(&bo->lock);
+ ret = ttm_bo_wait(bo, false, interruptible, no_wait_gpu);
+ spin_unlock(&bo->lock);
+
+ if (unlikely(ret != 0))
+ return ret;
+
spin_lock(&glob->lru_lock);
- if (list_empty(&bo->ddestroy)) {
- void *sync_obj = bo->sync_obj;
- void *sync_obj_arg = bo->sync_obj_arg;
+ ret = ttm_bo_reserve_locked(bo, interruptible,
+ no_wait_reserve, false, 0);
- kref_get(&bo->list_kref);
- list_add_tail(&bo->ddestroy, &bdev->ddestroy);
+ if (unlikely(ret != 0) || list_empty(&bo->ddestroy)) {
spin_unlock(&glob->lru_lock);
- spin_unlock(&bo->lock);
+ return ret;
+ }
- if (sync_obj)
- driver->sync_obj_flush(sync_obj, sync_obj_arg);
- schedule_delayed_work(&bdev->wq,
- ((HZ / 100) < 1) ? 1 : HZ / 100);
- ret = 0;
+ /**
+ * We can re-check for sync object without taking
+ * the bo::lock since setting the sync object requires
+ * also bo::reserved. A busy object at this point may
+ * be caused by another thread recently starting an accelerated
+ * eviction.
+ */
- } else {
+ if (unlikely(bo->sync_obj)) {
+ atomic_set(&bo->reserved, 0);
+ wake_up_all(&bo->event_queue);
spin_unlock(&glob->lru_lock);
- spin_unlock(&bo->lock);
- ret = -EBUSY;
+ goto retry;
}
- return ret;
+ put_count = ttm_bo_del_from_lru(bo);
+ list_del_init(&bo->ddestroy);
+ ++put_count;
+
+ spin_unlock(&glob->lru_lock);
+ ttm_bo_cleanup_memtype_use(bo);
+
+ while (put_count--)
+ kref_put(&bo->list_kref, ttm_bo_ref_bug);
+
+ return 0;
}
/**
@@ -602,7 +603,8 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
}
spin_unlock(&glob->lru_lock);
- ret = ttm_bo_cleanup_refs(entry, remove_all);
+ ret = ttm_bo_cleanup_refs(entry, false, !remove_all,
+ !remove_all);
kref_put(&entry->list_kref, ttm_bo_release_list);
entry = nentry;
@@ -645,7 +647,7 @@ static void ttm_bo_release(struct kref *kref)
bo->vm_node = NULL;
}
write_unlock(&bdev->vm_lock);
- ttm_bo_cleanup_refs(bo, false);
+ ttm_bo_cleanup_refs_or_queue(bo);
kref_put(&bo->list_kref, ttm_bo_release_list);
write_lock(&bdev->vm_lock);
}
@@ -680,7 +682,6 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
bool no_wait_reserve, bool no_wait_gpu)
{
struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_bo_global *glob = bo->glob;
struct ttm_mem_reg evict_mem;
struct ttm_placement placement;
int ret = 0;
@@ -726,12 +727,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
if (ret) {
if (ret != -ERESTARTSYS)
printk(KERN_ERR TTM_PFX "Buffer eviction failed\n");
- spin_lock(&glob->lru_lock);
- if (evict_mem.mm_node) {
- drm_mm_put_block(evict_mem.mm_node);
- evict_mem.mm_node = NULL;
- }
- spin_unlock(&glob->lru_lock);
+ ttm_bo_mem_put(bo, &evict_mem);
goto out;
}
bo->evicted = true;
@@ -759,6 +755,18 @@ retry:
bo = list_first_entry(&man->lru, struct ttm_buffer_object, lru);
kref_get(&bo->list_kref);
+ if (!list_empty(&bo->ddestroy)) {
+ spin_unlock(&glob->lru_lock);
+ ret = ttm_bo_cleanup_refs(bo, interruptible,
+ no_wait_reserve, no_wait_gpu);
+ kref_put(&bo->list_kref, ttm_bo_release_list);
+
+ if (likely(ret == 0 || ret == -ERESTARTSYS))
+ return ret;
+
+ goto retry;
+ }
+
ret = ttm_bo_reserve_locked(bo, false, no_wait_reserve, false, 0);
if (unlikely(ret == -EBUSY)) {
@@ -792,41 +800,14 @@ retry:
return ret;
}
-static int ttm_bo_man_get_node(struct ttm_buffer_object *bo,
- struct ttm_mem_type_manager *man,
- struct ttm_placement *placement,
- struct ttm_mem_reg *mem,
- struct drm_mm_node **node)
+void ttm_bo_mem_put(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem)
{
- struct ttm_bo_global *glob = bo->glob;
- unsigned long lpfn;
- int ret;
+ struct ttm_mem_type_manager *man = &bo->bdev->man[mem->mem_type];
- lpfn = placement->lpfn;
- if (!lpfn)
- lpfn = man->size;
- *node = NULL;
- do {
- ret = drm_mm_pre_get(&man->manager);
- if (unlikely(ret))
- return ret;
-
- spin_lock(&glob->lru_lock);
- *node = drm_mm_search_free_in_range(&man->manager,
- mem->num_pages, mem->page_alignment,
- placement->fpfn, lpfn, 1);
- if (unlikely(*node == NULL)) {
- spin_unlock(&glob->lru_lock);
- return 0;
- }
- *node = drm_mm_get_block_atomic_range(*node, mem->num_pages,
- mem->page_alignment,
- placement->fpfn,
- lpfn);
- spin_unlock(&glob->lru_lock);
- } while (*node == NULL);
- return 0;
+ if (mem->mm_node)
+ (*man->func->put_node)(man, mem);
}
+EXPORT_SYMBOL(ttm_bo_mem_put);
/**
* Repeatedly evict memory from the LRU for @mem_type until we create enough
@@ -843,14 +824,13 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo,
struct ttm_bo_device *bdev = bo->bdev;
struct ttm_bo_global *glob = bdev->glob;
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
- struct drm_mm_node *node;
int ret;
do {
- ret = ttm_bo_man_get_node(bo, man, placement, mem, &node);
+ ret = (*man->func->get_node)(man, bo, placement, mem);
if (unlikely(ret != 0))
return ret;
- if (node)
+ if (mem->mm_node)
break;
spin_lock(&glob->lru_lock);
if (list_empty(&man->lru)) {
@@ -863,9 +843,8 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo,
if (unlikely(ret != 0))
return ret;
} while (1);
- if (node == NULL)
+ if (mem->mm_node == NULL)
return -ENOMEM;
- mem->mm_node = node;
mem->mem_type = mem_type;
return 0;
}
@@ -939,7 +918,6 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
bool type_found = false;
bool type_ok = false;
bool has_erestartsys = false;
- struct drm_mm_node *node = NULL;
int i, ret;
mem->mm_node = NULL;
@@ -973,17 +951,15 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
if (man->has_type && man->use_type) {
type_found = true;
- ret = ttm_bo_man_get_node(bo, man, placement, mem,
- &node);
+ ret = (*man->func->get_node)(man, bo, placement, mem);
if (unlikely(ret))
return ret;
}
- if (node)
+ if (mem->mm_node)
break;
}
- if ((type_ok && (mem_type == TTM_PL_SYSTEM)) || node) {
- mem->mm_node = node;
+ if ((type_ok && (mem_type == TTM_PL_SYSTEM)) || mem->mm_node) {
mem->mem_type = mem_type;
mem->placement = cur_flags;
return 0;
@@ -1053,7 +1029,6 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
bool interruptible, bool no_wait_reserve,
bool no_wait_gpu)
{
- struct ttm_bo_global *glob = bo->glob;
int ret = 0;
struct ttm_mem_reg mem;
@@ -1081,11 +1056,8 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
goto out_unlock;
ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait_reserve, no_wait_gpu);
out_unlock:
- if (ret && mem.mm_node) {
- spin_lock(&glob->lru_lock);
- drm_mm_put_block(mem.mm_node);
- spin_unlock(&glob->lru_lock);
- }
+ if (ret && mem.mm_node)
+ ttm_bo_mem_put(bo, &mem);
return ret;
}
@@ -1093,11 +1065,10 @@ static int ttm_bo_mem_compat(struct ttm_placement *placement,
struct ttm_mem_reg *mem)
{
int i;
- struct drm_mm_node *node = mem->mm_node;
- if (node && placement->lpfn != 0 &&
- (node->start < placement->fpfn ||
- node->start + node->size > placement->lpfn))
+ if (mem->mm_node && placement->lpfn != 0 &&
+ (mem->start < placement->fpfn ||
+ mem->start + mem->num_pages > placement->lpfn))
return -1;
for (i = 0; i < placement->num_placement; i++) {
@@ -1341,7 +1312,6 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
{
- struct ttm_bo_global *glob = bdev->glob;
struct ttm_mem_type_manager *man;
int ret = -EINVAL;
@@ -1364,13 +1334,7 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
if (mem_type > 0) {
ttm_bo_force_list_clean(bdev, mem_type, false);
- spin_lock(&glob->lru_lock);
- if (drm_mm_clean(&man->manager))
- drm_mm_takedown(&man->manager);
- else
- ret = -EBUSY;
-
- spin_unlock(&glob->lru_lock);
+ ret = (*man->func->takedown)(man);
}
return ret;
@@ -1421,6 +1385,7 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
ret = bdev->driver->init_mem_type(bdev, type, man);
if (ret)
return ret;
+ man->bdev = bdev;
ret = 0;
if (type != TTM_PL_SYSTEM) {
@@ -1430,7 +1395,8 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
type);
return ret;
}
- ret = drm_mm_init(&man->manager, 0, p_size);
+
+ ret = (*man->func->init)(man, p_size);
if (ret)
return ret;
}
@@ -1824,6 +1790,13 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
struct ttm_buffer_object, swap);
kref_get(&bo->list_kref);
+ if (!list_empty(&bo->ddestroy)) {
+ spin_unlock(&glob->lru_lock);
+ (void) ttm_bo_cleanup_refs(bo, false, false, false);
+ kref_put(&bo->list_kref, ttm_bo_release_list);
+ continue;
+ }
+
/**
* Reserve buffer. Since we unlock while sleeping, we need
* to re-check that nobody removed us from the swap-list while
diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c
new file mode 100644
index 000000000000..7410c190c891
--- /dev/null
+++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c
@@ -0,0 +1,148 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
+ * 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
+ * THE COPYRIGHT HOLDERS, AUTHORS 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: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
+ */
+
+#include "ttm/ttm_module.h"
+#include "ttm/ttm_bo_driver.h"
+#include "ttm/ttm_placement.h"
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/module.h>
+
+static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
+ struct ttm_buffer_object *bo,
+ struct ttm_placement *placement,
+ struct ttm_mem_reg *mem)
+{
+ struct ttm_bo_global *glob = man->bdev->glob;
+ struct drm_mm *mm = man->priv;
+ struct drm_mm_node *node = NULL;
+ unsigned long lpfn;
+ int ret;
+
+ lpfn = placement->lpfn;
+ if (!lpfn)
+ lpfn = man->size;
+ do {
+ ret = drm_mm_pre_get(mm);
+ if (unlikely(ret))
+ return ret;
+
+ spin_lock(&glob->lru_lock);
+ node = drm_mm_search_free_in_range(mm,
+ mem->num_pages, mem->page_alignment,
+ placement->fpfn, lpfn, 1);
+ if (unlikely(node == NULL)) {
+ spin_unlock(&glob->lru_lock);
+ return 0;
+ }
+ node = drm_mm_get_block_atomic_range(node, mem->num_pages,
+ mem->page_alignment,
+ placement->fpfn,
+ lpfn);
+ spin_unlock(&glob->lru_lock);
+ } while (node == NULL);
+
+ mem->mm_node = node;
+ mem->start = node->start;
+ return 0;
+}
+
+static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man,
+ struct ttm_mem_reg *mem)
+{
+ struct ttm_bo_global *glob = man->bdev->glob;
+
+ if (mem->mm_node) {
+ spin_lock(&glob->lru_lock);
+ drm_mm_put_block(mem->mm_node);
+ spin_unlock(&glob->lru_lock);
+ mem->mm_node = NULL;
+ }
+}
+
+static int ttm_bo_man_init(struct ttm_mem_type_manager *man,
+ unsigned long p_size)
+{
+ struct drm_mm *mm;
+ int ret;
+
+ mm = kzalloc(sizeof(*mm), GFP_KERNEL);
+ if (!mm)
+ return -ENOMEM;
+
+ ret = drm_mm_init(mm, 0, p_size);
+ if (ret) {
+ kfree(mm);
+ return ret;
+ }
+
+ man->priv = mm;
+ return 0;
+}
+
+static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man)
+{
+ struct ttm_bo_global *glob = man->bdev->glob;
+ struct drm_mm *mm = man->priv;
+ int ret = 0;
+
+ spin_lock(&glob->lru_lock);
+ if (drm_mm_clean(mm)) {
+ drm_mm_takedown(mm);
+ kfree(mm);
+ man->priv = NULL;
+ } else
+ ret = -EBUSY;
+ spin_unlock(&glob->lru_lock);
+ return ret;
+}
+
+static void ttm_bo_man_debug(struct ttm_mem_type_manager *man,
+ const char *prefix)
+{
+ struct ttm_bo_global *glob = man->bdev->glob;
+ struct drm_mm *mm = man->priv;
+
+ spin_lock(&glob->lru_lock);
+ drm_mm_debug_table(mm, prefix);
+ spin_unlock(&glob->lru_lock);
+}
+
+const struct ttm_mem_type_manager_func ttm_bo_manager_func = {
+ ttm_bo_man_init,
+ ttm_bo_man_takedown,
+ ttm_bo_man_get_node,
+ ttm_bo_man_put_node,
+ ttm_bo_man_debug
+};
+EXPORT_SYMBOL(ttm_bo_manager_func);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 3451a82adba7..3106d5bcce32 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -39,14 +39,7 @@
void ttm_bo_free_old_node(struct ttm_buffer_object *bo)
{
- struct ttm_mem_reg *old_mem = &bo->mem;
-
- if (old_mem->mm_node) {
- spin_lock(&bo->glob->lru_lock);
- drm_mm_put_block(old_mem->mm_node);
- spin_unlock(&bo->glob->lru_lock);
- }
- old_mem->mm_node = NULL;
+ ttm_bo_mem_put(bo, &bo->mem);
}
int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
@@ -170,7 +163,7 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
src = (void *)((unsigned long)src + (page << PAGE_SHIFT));
#ifdef CONFIG_X86
- dst = kmap_atomic_prot(d, KM_USER0, prot);
+ dst = kmap_atomic_prot(d, prot);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
dst = vmap(&d, 1, 0, prot);
@@ -183,7 +176,7 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
memcpy_fromio(dst, src, PAGE_SIZE);
#ifdef CONFIG_X86
- kunmap_atomic(dst, KM_USER0);
+ kunmap_atomic(dst);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(dst);
@@ -206,7 +199,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst,
dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT));
#ifdef CONFIG_X86
- src = kmap_atomic_prot(s, KM_USER0, prot);
+ src = kmap_atomic_prot(s, prot);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
src = vmap(&s, 1, 0, prot);
@@ -219,7 +212,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst,
memcpy_toio(dst, src, PAGE_SIZE);
#ifdef CONFIG_X86
- kunmap_atomic(src, KM_USER0);
+ kunmap_atomic(src);
#else
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(src);
@@ -263,8 +256,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
dir = 1;
if ((old_mem->mem_type == new_mem->mem_type) &&
- (new_mem->mm_node->start <
- old_mem->mm_node->start + old_mem->mm_node->size)) {
+ (new_mem->start < old_mem->start + old_mem->size)) {
dir = -1;
add = new_mem->num_pages - 1;
}
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
index b8984a5ae521..e1ff4e7a6eb0 100644
--- a/drivers/gpu/drm/via/via_drv.c
+++ b/drivers/gpu/drm/via/via_drv.c
@@ -51,8 +51,6 @@ static struct drm_driver driver = {
.reclaim_buffers_locked = NULL,
.reclaim_buffers_idlelocked = via_reclaim_buffers_locked,
.lastclose = via_lastclose,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = via_ioctls,
.fops = {
.owner = THIS_MODULE,
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile
index 4505e17df3f5..c9281a1b1d3b 100644
--- a/drivers/gpu/drm/vmwgfx/Makefile
+++ b/drivers/gpu/drm/vmwgfx/Makefile
@@ -4,6 +4,6 @@ ccflags-y := -Iinclude/drm
vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \
vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \
- vmwgfx_overlay.o vmwgfx_fence.o
+ vmwgfx_overlay.o vmwgfx_fence.o vmwgfx_gmrid_manager.o
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
index c4f5114aee7c..80bc37b274e7 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
@@ -39,6 +39,9 @@ static uint32_t vram_ne_placement_flags = TTM_PL_FLAG_VRAM |
static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |
TTM_PL_FLAG_CACHED;
+static uint32_t gmr_placement_flags = VMW_PL_FLAG_GMR |
+ TTM_PL_FLAG_CACHED;
+
struct ttm_placement vmw_vram_placement = {
.fpfn = 0,
.lpfn = 0,
@@ -48,6 +51,20 @@ struct ttm_placement vmw_vram_placement = {
.busy_placement = &vram_placement_flags
};
+static uint32_t vram_gmr_placement_flags[] = {
+ TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED,
+ VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+};
+
+struct ttm_placement vmw_vram_gmr_placement = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .num_placement = 2,
+ .placement = vram_gmr_placement_flags,
+ .num_busy_placement = 1,
+ .busy_placement = &gmr_placement_flags
+};
+
struct ttm_placement vmw_vram_sys_placement = {
.fpfn = 0,
.lpfn = 0,
@@ -77,27 +94,52 @@ struct ttm_placement vmw_sys_placement = {
struct vmw_ttm_backend {
struct ttm_backend backend;
+ struct page **pages;
+ unsigned long num_pages;
+ struct vmw_private *dev_priv;
+ int gmr_id;
};
static int vmw_ttm_populate(struct ttm_backend *backend,
unsigned long num_pages, struct page **pages,
struct page *dummy_read_page)
{
+ struct vmw_ttm_backend *vmw_be =
+ container_of(backend, struct vmw_ttm_backend, backend);
+
+ vmw_be->pages = pages;
+ vmw_be->num_pages = num_pages;
+
return 0;
}
static int vmw_ttm_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem)
{
- return 0;
+ struct vmw_ttm_backend *vmw_be =
+ container_of(backend, struct vmw_ttm_backend, backend);
+
+ vmw_be->gmr_id = bo_mem->start;
+
+ return vmw_gmr_bind(vmw_be->dev_priv, vmw_be->pages,
+ vmw_be->num_pages, vmw_be->gmr_id);
}
static int vmw_ttm_unbind(struct ttm_backend *backend)
{
+ struct vmw_ttm_backend *vmw_be =
+ container_of(backend, struct vmw_ttm_backend, backend);
+
+ vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id);
return 0;
}
static void vmw_ttm_clear(struct ttm_backend *backend)
{
+ struct vmw_ttm_backend *vmw_be =
+ container_of(backend, struct vmw_ttm_backend, backend);
+
+ vmw_be->pages = NULL;
+ vmw_be->num_pages = 0;
}
static void vmw_ttm_destroy(struct ttm_backend *backend)
@@ -125,6 +167,7 @@ struct ttm_backend *vmw_ttm_backend_init(struct ttm_bo_device *bdev)
return NULL;
vmw_be->backend.func = &vmw_ttm_func;
+ vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev);
return &vmw_be->backend;
}
@@ -142,15 +185,28 @@ int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
/* System memory */
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
- man->available_caching = TTM_PL_MASK_CACHING;
+ man->available_caching = TTM_PL_FLAG_CACHED;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
/* "On-card" video ram */
+ man->func = &ttm_bo_manager_func;
man->gpu_offset = 0;
man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_MAPPABLE;
- man->available_caching = TTM_PL_MASK_CACHING;
- man->default_caching = TTM_PL_FLAG_WC;
+ man->available_caching = TTM_PL_FLAG_CACHED;
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ break;
+ case VMW_PL_GMR:
+ /*
+ * "Guest Memory Regions" is an aperture like feature with
+ * one slot per bo. There is an upper limit of the number of
+ * slots as well as the bo size.
+ */
+ man->func = &vmw_gmrid_manager_func;
+ man->gpu_offset = 0;
+ man->flags = TTM_MEMTYPE_FLAG_CMA | TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_FLAG_CACHED;
+ man->default_caching = TTM_PL_FLAG_CACHED;
break;
default:
DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
@@ -174,18 +230,6 @@ static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp)
return 0;
}
-static void vmw_move_notify(struct ttm_buffer_object *bo,
- struct ttm_mem_reg *new_mem)
-{
- if (new_mem->mem_type != TTM_PL_SYSTEM)
- vmw_dmabuf_gmr_unbind(bo);
-}
-
-static void vmw_swap_notify(struct ttm_buffer_object *bo)
-{
- vmw_dmabuf_gmr_unbind(bo);
-}
-
static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
{
struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
@@ -200,10 +244,10 @@ static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg
return -EINVAL;
switch (mem->mem_type) {
case TTM_PL_SYSTEM:
- /* System memory */
+ case VMW_PL_GMR:
return 0;
case TTM_PL_VRAM:
- mem->bus.offset = mem->mm_node->start << PAGE_SHIFT;
+ mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = dev_priv->vram_start;
mem->bus.is_iomem = true;
break;
@@ -276,8 +320,8 @@ struct ttm_bo_driver vmw_bo_driver = {
.sync_obj_flush = vmw_sync_obj_flush,
.sync_obj_unref = vmw_sync_obj_unref,
.sync_obj_ref = vmw_sync_obj_ref,
- .move_notify = vmw_move_notify,
- .swap_notify = vmw_swap_notify,
+ .move_notify = NULL,
+ .swap_notify = NULL,
.fault_reserve_notify = &vmw_ttm_fault_reserve_notify,
.io_mem_reserve = &vmw_ttm_io_mem_reserve,
.io_mem_free = &vmw_ttm_io_mem_free,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 2ef93df9e8ae..10ca97ee0206 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -260,13 +260,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
idr_init(&dev_priv->context_idr);
idr_init(&dev_priv->surface_idr);
idr_init(&dev_priv->stream_idr);
- ida_init(&dev_priv->gmr_ida);
mutex_init(&dev_priv->init_mutex);
init_waitqueue_head(&dev_priv->fence_queue);
init_waitqueue_head(&dev_priv->fifo_queue);
atomic_set(&dev_priv->fence_queue_waiters, 0);
atomic_set(&dev_priv->fifo_queue_waiters, 0);
- INIT_LIST_HEAD(&dev_priv->gmr_lru);
dev_priv->io_start = pci_resource_start(dev->pdev, 0);
dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
@@ -341,6 +339,14 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
goto out_err2;
}
+ dev_priv->has_gmr = true;
+ if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR,
+ dev_priv->max_gmr_ids) != 0) {
+ DRM_INFO("No GMR memory available. "
+ "Graphics memory resources are very limited.\n");
+ dev_priv->has_gmr = false;
+ }
+
dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start,
dev_priv->mmio_size, DRM_MTRR_WC);
@@ -440,13 +446,14 @@ out_err4:
out_err3:
drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
dev_priv->mmio_size, DRM_MTRR_WC);
+ if (dev_priv->has_gmr)
+ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
(void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
out_err2:
(void)ttm_bo_device_release(&dev_priv->bdev);
out_err1:
vmw_ttm_global_release(dev_priv);
out_err0:
- ida_destroy(&dev_priv->gmr_ida);
idr_destroy(&dev_priv->surface_idr);
idr_destroy(&dev_priv->context_idr);
idr_destroy(&dev_priv->stream_idr);
@@ -478,10 +485,11 @@ static int vmw_driver_unload(struct drm_device *dev)
iounmap(dev_priv->mmio_virt);
drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
dev_priv->mmio_size, DRM_MTRR_WC);
+ if (dev_priv->has_gmr)
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
(void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
(void)ttm_bo_device_release(&dev_priv->bdev);
vmw_ttm_global_release(dev_priv);
- ida_destroy(&dev_priv->gmr_ida);
idr_destroy(&dev_priv->surface_idr);
idr_destroy(&dev_priv->context_idr);
idr_destroy(&dev_priv->stream_idr);
@@ -597,6 +605,8 @@ static void vmw_lastclose(struct drm_device *dev)
static void vmw_master_init(struct vmw_master *vmaster)
{
ttm_lock_init(&vmaster->lock);
+ INIT_LIST_HEAD(&vmaster->fb_surf);
+ mutex_init(&vmaster->fb_surf_mutex);
}
static int vmw_master_create(struct drm_device *dev,
@@ -608,7 +618,7 @@ static int vmw_master_create(struct drm_device *dev,
if (unlikely(vmaster == NULL))
return -ENOMEM;
- ttm_lock_init(&vmaster->lock);
+ vmw_master_init(vmaster);
ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
master->driver_priv = vmaster;
@@ -699,6 +709,7 @@ static void vmw_master_drop(struct drm_device *dev,
vmw_fp->locked_master = drm_master_get(file_priv->master);
ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
+ vmw_kms_idle_workqueues(vmaster);
if (unlikely((ret != 0))) {
DRM_ERROR("Unable to lock TTM at VT switch.\n");
@@ -751,15 +762,16 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
* Buffer contents is moved to swappable memory.
*/
ttm_bo_swapout_all(&dev_priv->bdev);
+
break;
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
+ case PM_POST_RESTORE:
ttm_suspend_unlock(&vmaster->lock);
+
break;
case PM_RESTORE_PREPARE:
break;
- case PM_POST_RESTORE:
- break;
default:
break;
}
@@ -770,21 +782,98 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
* These might not be needed with the virtual SVGA device.
*/
-int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+ if (dev_priv->num_3d_resources != 0) {
+ DRM_INFO("Can't suspend or hibernate "
+ "while 3D resources are active.\n");
+ return -EBUSY;
+ }
+
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
-int vmw_pci_resume(struct pci_dev *pdev)
+static int vmw_pci_resume(struct pci_dev *pdev)
{
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
return pci_enable_device(pdev);
}
+static int vmw_pm_suspend(struct device *kdev)
+{
+ struct pci_dev *pdev = to_pci_dev(kdev);
+ struct pm_message dummy;
+
+ dummy.event = 0;
+
+ return vmw_pci_suspend(pdev, dummy);
+}
+
+static int vmw_pm_resume(struct device *kdev)
+{
+ struct pci_dev *pdev = to_pci_dev(kdev);
+
+ return vmw_pci_resume(pdev);
+}
+
+static int vmw_pm_prepare(struct device *kdev)
+{
+ struct pci_dev *pdev = to_pci_dev(kdev);
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+ /**
+ * Release 3d reference held by fbdev and potentially
+ * stop fifo.
+ */
+ dev_priv->suspended = true;
+ if (dev_priv->enable_fb)
+ vmw_3d_resource_dec(dev_priv);
+
+ if (dev_priv->num_3d_resources != 0) {
+
+ DRM_INFO("Can't suspend or hibernate "
+ "while 3D resources are active.\n");
+
+ if (dev_priv->enable_fb)
+ vmw_3d_resource_inc(dev_priv);
+ dev_priv->suspended = false;
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void vmw_pm_complete(struct device *kdev)
+{
+ struct pci_dev *pdev = to_pci_dev(kdev);
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+ /**
+ * Reclaim 3d reference held by fbdev and potentially
+ * start fifo.
+ */
+ if (dev_priv->enable_fb)
+ vmw_3d_resource_inc(dev_priv);
+
+ dev_priv->suspended = false;
+}
+
+static const struct dev_pm_ops vmw_pm_ops = {
+ .prepare = vmw_pm_prepare,
+ .complete = vmw_pm_complete,
+ .suspend = vmw_pm_suspend,
+ .resume = vmw_pm_resume,
+};
+
static struct drm_driver driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
DRIVER_MODESET,
@@ -798,8 +887,6 @@ static struct drm_driver driver = {
.irq_handler = vmw_irq_handler,
.get_vblank_counter = vmw_get_vblank_counter,
.reclaim_buffers_locked = NULL,
- .get_map_ofs = drm_core_get_map_ofs,
- .get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = vmw_ioctls,
.num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls),
.dma_quiescent = NULL, /*vmw_dma_quiescent, */
@@ -821,15 +908,16 @@ static struct drm_driver driver = {
.compat_ioctl = drm_compat_ioctl,
#endif
.llseek = noop_llseek,
- },
+ },
.pci_driver = {
- .name = VMWGFX_DRIVER_NAME,
- .id_table = vmw_pci_id_list,
- .probe = vmw_probe,
- .remove = vmw_remove,
- .suspend = vmw_pci_suspend,
- .resume = vmw_pci_resume
- },
+ .name = VMWGFX_DRIVER_NAME,
+ .id_table = vmw_pci_id_list,
+ .probe = vmw_probe,
+ .remove = vmw_remove,
+ .driver = {
+ .pm = &vmw_pm_ops
+ }
+ },
.name = VMWGFX_DRIVER_NAME,
.desc = VMWGFX_DRIVER_DESC,
.date = VMWGFX_DRIVER_DATE,
@@ -863,3 +951,7 @@ module_exit(vmwgfx_exit);
MODULE_AUTHOR("VMware Inc. and others");
MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device");
MODULE_LICENSE("GPL and additional rights");
+MODULE_VERSION(__stringify(VMWGFX_DRIVER_MAJOR) "."
+ __stringify(VMWGFX_DRIVER_MINOR) "."
+ __stringify(VMWGFX_DRIVER_PATCHLEVEL) "."
+ "0");
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index 58de6393f611..e7a58d055041 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -39,9 +39,9 @@
#include "ttm/ttm_execbuf_util.h"
#include "ttm/ttm_module.h"
-#define VMWGFX_DRIVER_DATE "20100209"
+#define VMWGFX_DRIVER_DATE "20100927"
#define VMWGFX_DRIVER_MAJOR 1
-#define VMWGFX_DRIVER_MINOR 2
+#define VMWGFX_DRIVER_MINOR 4
#define VMWGFX_DRIVER_PATCHLEVEL 0
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
@@ -49,6 +49,9 @@
#define VMWGFX_MAX_GMRS 2048
#define VMWGFX_MAX_DISPLAYS 16
+#define VMW_PL_GMR TTM_PL_PRIV0
+#define VMW_PL_FLAG_GMR TTM_PL_FLAG_PRIV0
+
struct vmw_fpriv {
struct drm_master *locked_master;
struct ttm_object_file *tfile;
@@ -57,8 +60,6 @@ struct vmw_fpriv {
struct vmw_dma_buffer {
struct ttm_buffer_object base;
struct list_head validate_list;
- struct list_head gmr_lru;
- uint32_t gmr_id;
bool gmr_bound;
uint32_t cur_validate_node;
bool on_validate_list;
@@ -151,6 +152,8 @@ struct vmw_overlay;
struct vmw_master {
struct ttm_lock lock;
+ struct mutex fb_surf_mutex;
+ struct list_head fb_surf;
};
struct vmw_vga_topology_state {
@@ -182,6 +185,7 @@ struct vmw_private {
uint32_t capabilities;
uint32_t max_gmr_descriptors;
uint32_t max_gmr_ids;
+ bool has_gmr;
struct mutex hw_mutex;
/*
@@ -264,14 +268,6 @@ struct vmw_private {
struct mutex cmdbuf_mutex;
/**
- * GMR management. Protected by the lru spinlock.
- */
-
- struct ida gmr_ida;
- struct list_head gmr_lru;
-
-
- /**
* Operating mode.
*/
@@ -286,6 +282,7 @@ struct vmw_private {
struct vmw_master *active_master;
struct vmw_master fbdev_master;
struct notifier_block pm_nb;
+ bool suspended;
struct mutex release_mutex;
uint32_t num_3d_resources;
@@ -331,7 +328,9 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv);
*/
extern int vmw_gmr_bind(struct vmw_private *dev_priv,
- struct ttm_buffer_object *bo);
+ struct page *pages[],
+ unsigned long num_pages,
+ int gmr_id);
extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id);
/**
@@ -380,14 +379,10 @@ extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo);
extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
uint32_t id, struct vmw_dma_buffer **out);
-extern uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo);
-extern void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id);
-extern int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id);
extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
struct vmw_dma_buffer *bo);
extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
struct vmw_dma_buffer *bo);
-extern void vmw_dmabuf_gmr_unbind(struct ttm_buffer_object *bo);
extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
@@ -439,6 +434,7 @@ extern int vmw_mmap(struct file *filp, struct vm_area_struct *vma);
extern struct ttm_placement vmw_vram_placement;
extern struct ttm_placement vmw_vram_ne_placement;
extern struct ttm_placement vmw_vram_sys_placement;
+extern struct ttm_placement vmw_vram_gmr_placement;
extern struct ttm_placement vmw_sys_placement;
extern struct ttm_bo_driver vmw_bo_driver;
extern int vmw_dma_quiescent(struct drm_device *dev);
@@ -518,6 +514,10 @@ void vmw_kms_write_svga(struct vmw_private *vmw_priv,
unsigned bbp, unsigned depth);
int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+void vmw_kms_idle_workqueues(struct vmw_master *vmaster);
+bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
+ uint32_t pitch,
+ uint32_t height);
u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc);
/**
@@ -537,6 +537,12 @@ int vmw_overlay_num_overlays(struct vmw_private *dev_priv);
int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv);
/**
+ * GMR Id manager
+ */
+
+extern const struct ttm_mem_type_manager_func vmw_gmrid_manager_func;
+
+/**
* Inline helper functions
*/
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 8e396850513c..51d9f9f1d7f2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -538,8 +538,11 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context)
reloc = &sw_context->relocs[i];
validate = &sw_context->val_bufs[reloc->index];
bo = validate->bo;
- reloc->location->offset += bo->offset;
- reloc->location->gmrId = vmw_dmabuf_gmr(bo);
+ if (bo->mem.mem_type == TTM_PL_VRAM) {
+ reloc->location->offset += bo->offset;
+ reloc->location->gmrId = SVGA_GMR_FRAMEBUFFER;
+ } else
+ reloc->location->gmrId = bo->mem.start;
}
vmw_free_relocations(sw_context);
}
@@ -563,25 +566,14 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv,
{
int ret;
- if (vmw_dmabuf_gmr(bo) != SVGA_GMR_NULL)
- return 0;
-
/**
- * Put BO in VRAM, only if there is space.
+ * Put BO in VRAM if there is space, otherwise as a GMR.
+ * If there is no space in VRAM and GMR ids are all used up,
+ * start evicting GMRs to make room. If the DMA buffer can't be
+ * used as a GMR, this will return -ENOMEM.
*/
- ret = ttm_bo_validate(bo, &vmw_vram_sys_placement, true, false, false);
- if (unlikely(ret == -ERESTARTSYS))
- return ret;
-
- /**
- * Otherwise, set it up as GMR.
- */
-
- if (vmw_dmabuf_gmr(bo) != SVGA_GMR_NULL)
- return 0;
-
- ret = vmw_gmr_bind(dev_priv, bo);
+ ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, true, false, false);
if (likely(ret == 0 || ret == -ERESTARTSYS))
return ret;
@@ -590,6 +582,7 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv,
* previous contents.
*/
+ DRM_INFO("Falling through to VRAM.\n");
ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false, false);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 409e172f4abf..41d9a5b73c03 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -144,6 +144,13 @@ static int vmw_fb_check_var(struct fb_var_screeninfo *var,
return -EINVAL;
}
+ if (!vmw_kms_validate_mode_vram(vmw_priv,
+ info->fix.line_length,
+ var->yoffset + var->yres)) {
+ DRM_ERROR("Requested geom can not fit in framebuffer\n");
+ return -EINVAL;
+ }
+
return 0;
}
@@ -205,6 +212,9 @@ static void vmw_fb_dirty_flush(struct vmw_fb_par *par)
SVGAFifoCmdUpdate body;
} *cmd;
+ if (vmw_priv->suspended)
+ return;
+
spin_lock_irqsave(&par->dirty.lock, flags);
if (!par->dirty.active) {
spin_unlock_irqrestore(&par->dirty.lock, flags);
@@ -616,7 +626,8 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
goto err_unlock;
if (bo->mem.mem_type == TTM_PL_VRAM &&
- bo->mem.mm_node->start < bo->num_pages)
+ bo->mem.start < bo->num_pages &&
+ bo->mem.start > 0)
(void) ttm_bo_validate(bo, &vmw_sys_placement, false,
false, false);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
index 5f8908a5d7fd..de0c5948521d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
@@ -146,7 +146,7 @@ static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv,
*/
static unsigned long vmw_gmr_count_descriptors(struct page *pages[],
- unsigned long num_pages)
+ unsigned long num_pages)
{
unsigned long prev_pfn = ~(0UL);
unsigned long pfn;
@@ -163,45 +163,33 @@ static unsigned long vmw_gmr_count_descriptors(struct page *pages[],
}
int vmw_gmr_bind(struct vmw_private *dev_priv,
- struct ttm_buffer_object *bo)
+ struct page *pages[],
+ unsigned long num_pages,
+ int gmr_id)
{
- struct ttm_tt *ttm = bo->ttm;
- unsigned long descriptors;
- int ret;
- uint32_t id;
struct list_head desc_pages;
+ int ret;
- if (!(dev_priv->capabilities & SVGA_CAP_GMR))
+ if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR)))
return -EINVAL;
- ret = ttm_tt_populate(ttm);
- if (unlikely(ret != 0))
- return ret;
-
- descriptors = vmw_gmr_count_descriptors(ttm->pages, ttm->num_pages);
- if (unlikely(descriptors > dev_priv->max_gmr_descriptors))
+ if (vmw_gmr_count_descriptors(pages, num_pages) >
+ dev_priv->max_gmr_descriptors)
return -EINVAL;
INIT_LIST_HEAD(&desc_pages);
- ret = vmw_gmr_build_descriptors(&desc_pages, ttm->pages,
- ttm->num_pages);
- if (unlikely(ret != 0))
- return ret;
- ret = vmw_gmr_id_alloc(dev_priv, &id);
+ ret = vmw_gmr_build_descriptors(&desc_pages, pages, num_pages);
if (unlikely(ret != 0))
- goto out_no_id;
+ return ret;
- vmw_gmr_fire_descriptors(dev_priv, id, &desc_pages);
+ vmw_gmr_fire_descriptors(dev_priv, gmr_id, &desc_pages);
vmw_gmr_free_descriptors(&desc_pages);
- vmw_dmabuf_set_gmr(bo, id);
- return 0;
-out_no_id:
- vmw_gmr_free_descriptors(&desc_pages);
- return ret;
+ return 0;
}
+
void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id)
{
mutex_lock(&dev_priv->hw_mutex);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
new file mode 100644
index 000000000000..ac6e0d1bd629
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
@@ -0,0 +1,137 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2007-2010 VMware, Inc., Palo Alto, CA., USA
+ * 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
+ * THE COPYRIGHT HOLDERS, AUTHORS 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: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
+ */
+
+#include "vmwgfx_drv.h"
+#include "ttm/ttm_module.h"
+#include "ttm/ttm_bo_driver.h"
+#include "ttm/ttm_placement.h"
+#include <linux/idr.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+
+struct vmwgfx_gmrid_man {
+ spinlock_t lock;
+ struct ida gmr_ida;
+ uint32_t max_gmr_ids;
+};
+
+static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man,
+ struct ttm_buffer_object *bo,
+ struct ttm_placement *placement,
+ struct ttm_mem_reg *mem)
+{
+ struct vmwgfx_gmrid_man *gman =
+ (struct vmwgfx_gmrid_man *)man->priv;
+ int ret;
+ int id;
+
+ mem->mm_node = NULL;
+
+ do {
+ if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0))
+ return -ENOMEM;
+
+ spin_lock(&gman->lock);
+ ret = ida_get_new(&gman->gmr_ida, &id);
+
+ if (unlikely(ret == 0 && id >= gman->max_gmr_ids)) {
+ ida_remove(&gman->gmr_ida, id);
+ spin_unlock(&gman->lock);
+ return 0;
+ }
+
+ spin_unlock(&gman->lock);
+
+ } while (ret == -EAGAIN);
+
+ if (likely(ret == 0)) {
+ mem->mm_node = gman;
+ mem->start = id;
+ }
+
+ return ret;
+}
+
+static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man,
+ struct ttm_mem_reg *mem)
+{
+ struct vmwgfx_gmrid_man *gman =
+ (struct vmwgfx_gmrid_man *)man->priv;
+
+ if (mem->mm_node) {
+ spin_lock(&gman->lock);
+ ida_remove(&gman->gmr_ida, mem->start);
+ spin_unlock(&gman->lock);
+ mem->mm_node = NULL;
+ }
+}
+
+static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man,
+ unsigned long p_size)
+{
+ struct vmwgfx_gmrid_man *gman =
+ kzalloc(sizeof(*gman), GFP_KERNEL);
+
+ if (unlikely(gman == NULL))
+ return -ENOMEM;
+
+ spin_lock_init(&gman->lock);
+ ida_init(&gman->gmr_ida);
+ gman->max_gmr_ids = p_size;
+ man->priv = (void *) gman;
+ return 0;
+}
+
+static int vmw_gmrid_man_takedown(struct ttm_mem_type_manager *man)
+{
+ struct vmwgfx_gmrid_man *gman =
+ (struct vmwgfx_gmrid_man *)man->priv;
+
+ if (gman) {
+ ida_destroy(&gman->gmr_ida);
+ kfree(gman);
+ }
+ return 0;
+}
+
+static void vmw_gmrid_man_debug(struct ttm_mem_type_manager *man,
+ const char *prefix)
+{
+ printk(KERN_INFO "%s: No debug info available for the GMR "
+ "id manager.\n", prefix);
+}
+
+const struct ttm_mem_type_manager_func vmw_gmrid_manager_func = {
+ vmw_gmrid_man_init,
+ vmw_gmrid_man_takedown,
+ vmw_gmrid_man_get_node,
+ vmw_gmrid_man_put_node,
+ vmw_gmrid_man_debug
+};
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
index 1c7a316454d8..570d57775a58 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
@@ -54,6 +54,9 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,
case DRM_VMW_PARAM_FIFO_CAPS:
param->value = dev_priv->fifo.capabilities;
break;
+ case DRM_VMW_PARAM_MAX_FB_SIZE:
+ param->value = dev_priv->vram_size;
+ break;
default:
DRM_ERROR("Illegal vmwgfx get param request: %d\n",
param->param);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index e882ba099f0c..87c6e6156d7d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -332,18 +332,55 @@ struct vmw_framebuffer_surface {
struct delayed_work d_work;
struct mutex work_lock;
bool present_fs;
+ struct list_head head;
+ struct drm_master *master;
};
+/**
+ * vmw_kms_idle_workqueues - Flush workqueues on this master
+ *
+ * @vmaster - Pointer identifying the master, for the surfaces of which
+ * we idle the dirty work queues.
+ *
+ * This function should be called with the ttm lock held in exclusive mode
+ * to idle all dirty work queues before the fifo is taken down.
+ *
+ * The work task may actually requeue itself, but after the flush returns we're
+ * sure that there's nothing to present, since the ttm lock is held in
+ * exclusive mode, so the fifo will never get used.
+ */
+
+void vmw_kms_idle_workqueues(struct vmw_master *vmaster)
+{
+ struct vmw_framebuffer_surface *entry;
+
+ mutex_lock(&vmaster->fb_surf_mutex);
+ list_for_each_entry(entry, &vmaster->fb_surf, head) {
+ if (cancel_delayed_work_sync(&entry->d_work))
+ (void) entry->d_work.work.func(&entry->d_work.work);
+
+ (void) cancel_delayed_work_sync(&entry->d_work);
+ }
+ mutex_unlock(&vmaster->fb_surf_mutex);
+}
+
void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
{
- struct vmw_framebuffer_surface *vfb =
+ struct vmw_framebuffer_surface *vfbs =
vmw_framebuffer_to_vfbs(framebuffer);
+ struct vmw_master *vmaster = vmw_master(vfbs->master);
+
- cancel_delayed_work_sync(&vfb->d_work);
+ mutex_lock(&vmaster->fb_surf_mutex);
+ list_del(&vfbs->head);
+ mutex_unlock(&vmaster->fb_surf_mutex);
+
+ cancel_delayed_work_sync(&vfbs->d_work);
+ drm_master_put(&vfbs->master);
drm_framebuffer_cleanup(framebuffer);
- vmw_surface_unreference(&vfb->surface);
+ vmw_surface_unreference(&vfbs->surface);
- kfree(framebuffer);
+ kfree(vfbs);
}
static void vmw_framebuffer_present_fs_callback(struct work_struct *work)
@@ -362,6 +399,12 @@ static void vmw_framebuffer_present_fs_callback(struct work_struct *work)
SVGA3dCopyRect cr;
} *cmd;
+ /**
+ * Strictly we should take the ttm_lock in read mode before accessing
+ * the fifo, to make sure the fifo is present and up. However,
+ * instead we flush all workqueues under the ttm lock in exclusive mode
+ * before taking down the fifo.
+ */
mutex_lock(&vfbs->work_lock);
if (!vfbs->present_fs)
goto out_unlock;
@@ -392,17 +435,20 @@ out_unlock:
int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
+ struct drm_file *file_priv,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
unsigned num_clips)
{
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
struct vmw_framebuffer_surface *vfbs =
vmw_framebuffer_to_vfbs(framebuffer);
struct vmw_surface *surf = vfbs->surface;
struct drm_clip_rect norect;
SVGA3dCopyRect *cr;
int i, inc = 1;
+ int ret;
struct {
SVGA3dCmdHeader header;
@@ -410,6 +456,13 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
SVGA3dCopyRect cr;
} *cmd;
+ if (unlikely(vfbs->master != file_priv->master))
+ return -EINVAL;
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ return ret;
+
if (!num_clips ||
!(dev_priv->fifo.capabilities &
SVGA_FIFO_CAP_SCREEN_OBJECT)) {
@@ -425,6 +478,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
*/
vmw_framebuffer_present_fs_callback(&vfbs->d_work.work);
}
+ ttm_read_unlock(&vmaster->lock);
return 0;
}
@@ -442,6 +496,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
+ ttm_read_unlock(&vmaster->lock);
return -ENOMEM;
}
@@ -461,7 +516,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
}
vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
-
+ ttm_read_unlock(&vmaster->lock);
return 0;
}
@@ -471,16 +526,57 @@ static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
.create_handle = vmw_framebuffer_create_handle,
};
-int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
- struct vmw_surface *surface,
- struct vmw_framebuffer **out,
- unsigned width, unsigned height)
+static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ struct vmw_surface *surface,
+ struct vmw_framebuffer **out,
+ const struct drm_mode_fb_cmd
+ *mode_cmd)
{
struct drm_device *dev = dev_priv->dev;
struct vmw_framebuffer_surface *vfbs;
+ enum SVGA3dSurfaceFormat format;
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
+ /*
+ * Sanity checks.
+ */
+
+ if (unlikely(surface->mip_levels[0] != 1 ||
+ surface->num_sizes != 1 ||
+ surface->sizes[0].width < mode_cmd->width ||
+ surface->sizes[0].height < mode_cmd->height ||
+ surface->sizes[0].depth != 1)) {
+ DRM_ERROR("Incompatible surface dimensions "
+ "for requested mode.\n");
+ return -EINVAL;
+ }
+
+ switch (mode_cmd->depth) {
+ case 32:
+ format = SVGA3D_A8R8G8B8;
+ break;
+ case 24:
+ format = SVGA3D_X8R8G8B8;
+ break;
+ case 16:
+ format = SVGA3D_R5G6B5;
+ break;
+ case 15:
+ format = SVGA3D_A1R5G5B5;
+ break;
+ default:
+ DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth);
+ return -EINVAL;
+ }
+
+ if (unlikely(format != surface->format)) {
+ DRM_ERROR("Invalid surface format for requested mode.\n");
+ return -EINVAL;
+ }
+
vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL);
if (!vfbs) {
ret = -ENOMEM;
@@ -498,16 +594,22 @@ int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
}
/* XXX get the first 3 from the surface info */
- vfbs->base.base.bits_per_pixel = 32;
- vfbs->base.base.pitch = width * 32 / 4;
- vfbs->base.base.depth = 24;
- vfbs->base.base.width = width;
- vfbs->base.base.height = height;
+ vfbs->base.base.bits_per_pixel = mode_cmd->bpp;
+ vfbs->base.base.pitch = mode_cmd->pitch;
+ vfbs->base.base.depth = mode_cmd->depth;
+ vfbs->base.base.width = mode_cmd->width;
+ vfbs->base.base.height = mode_cmd->height;
vfbs->base.pin = &vmw_surface_dmabuf_pin;
vfbs->base.unpin = &vmw_surface_dmabuf_unpin;
vfbs->surface = surface;
+ vfbs->master = drm_master_get(file_priv->master);
mutex_init(&vfbs->work_lock);
+
+ mutex_lock(&vmaster->fb_surf_mutex);
INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback);
+ list_add_tail(&vfbs->head, &vmaster->fb_surf);
+ mutex_unlock(&vmaster->fb_surf_mutex);
+
*out = &vfbs->base;
return 0;
@@ -544,18 +646,25 @@ void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
}
int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
+ struct drm_file *file_priv,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
unsigned num_clips)
{
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
struct drm_clip_rect norect;
+ int ret;
struct {
uint32_t header;
SVGAFifoCmdUpdate body;
} *cmd;
int i, increment = 1;
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ return ret;
+
if (!num_clips) {
num_clips = 1;
clips = &norect;
@@ -570,6 +679,7 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Fifo reserve failed.\n");
+ ttm_read_unlock(&vmaster->lock);
return -ENOMEM;
}
@@ -582,6 +692,7 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
}
vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips);
+ ttm_read_unlock(&vmaster->lock);
return 0;
}
@@ -659,16 +770,25 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer);
}
-int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
- struct vmw_dma_buffer *dmabuf,
- struct vmw_framebuffer **out,
- unsigned width, unsigned height)
+static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *dmabuf,
+ struct vmw_framebuffer **out,
+ const struct drm_mode_fb_cmd
+ *mode_cmd)
{
struct drm_device *dev = dev_priv->dev;
struct vmw_framebuffer_dmabuf *vfbd;
+ unsigned int requested_size;
int ret;
+ requested_size = mode_cmd->height * mode_cmd->pitch;
+ if (unlikely(requested_size > dmabuf->base.num_pages * PAGE_SIZE)) {
+ DRM_ERROR("Screen buffer object size is too small "
+ "for requested mode.\n");
+ return -EINVAL;
+ }
+
vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
if (!vfbd) {
ret = -ENOMEM;
@@ -685,12 +805,11 @@ int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
goto out_err3;
}
- /* XXX get the first 3 from the surface info */
- vfbd->base.base.bits_per_pixel = 32;
- vfbd->base.base.pitch = width * vfbd->base.base.bits_per_pixel / 8;
- vfbd->base.base.depth = 24;
- vfbd->base.base.width = width;
- vfbd->base.base.height = height;
+ vfbd->base.base.bits_per_pixel = mode_cmd->bpp;
+ vfbd->base.base.pitch = mode_cmd->pitch;
+ vfbd->base.base.depth = mode_cmd->depth;
+ vfbd->base.base.width = mode_cmd->width;
+ vfbd->base.base.height = mode_cmd->height;
vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
vfbd->buffer = dmabuf;
@@ -719,8 +838,25 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
struct vmw_framebuffer *vfb = NULL;
struct vmw_surface *surface = NULL;
struct vmw_dma_buffer *bo = NULL;
+ u64 required_size;
int ret;
+ /**
+ * This code should be conditioned on Screen Objects not being used.
+ * If screen objects are used, we can allocate a GMR to hold the
+ * requested framebuffer.
+ */
+
+ required_size = mode_cmd->pitch * mode_cmd->height;
+ if (unlikely(required_size > (u64) dev_priv->vram_size)) {
+ DRM_ERROR("VRAM size is too small for requested mode.\n");
+ return NULL;
+ }
+
+ /**
+ * End conditioned code.
+ */
+
ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
mode_cmd->handle, &surface);
if (ret)
@@ -729,8 +865,8 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
if (!surface->scanout)
goto err_not_scanout;
- ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
- mode_cmd->width, mode_cmd->height);
+ ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, surface,
+ &vfb, mode_cmd);
/* vmw_user_surface_lookup takes one ref so does new_fb */
vmw_surface_unreference(&surface);
@@ -751,7 +887,7 @@ try_dmabuf:
}
ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
- mode_cmd->width, mode_cmd->height);
+ mode_cmd);
/* vmw_user_dmabuf_lookup takes one ref so does new_fb */
vmw_dmabuf_unreference(&bo);
@@ -889,6 +1025,9 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv)
vmw_priv->num_displays = vmw_read(vmw_priv,
SVGA_REG_NUM_GUEST_DISPLAYS);
+ if (vmw_priv->num_displays == 0)
+ vmw_priv->num_displays = 1;
+
for (i = 0; i < vmw_priv->num_displays; ++i) {
save = &vmw_priv->vga_save[i];
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, i);
@@ -997,6 +1136,13 @@ out_unlock:
return ret;
}
+bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
+ uint32_t pitch,
+ uint32_t height)
+{
+ return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size;
+}
+
u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc)
{
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index 11cb39e3accb..a01c47ddb5bc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -427,7 +427,9 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector,
{
struct vmw_legacy_display_unit *ldu = vmw_connector_to_ldu(connector);
struct drm_device *dev = connector->dev;
+ struct vmw_private *dev_priv = vmw_priv(dev);
struct drm_display_mode *mode = NULL;
+ struct drm_display_mode *bmode;
struct drm_display_mode prefmode = { DRM_MODE("preferred",
DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -443,22 +445,30 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector,
mode->hdisplay = ldu->pref_width;
mode->vdisplay = ldu->pref_height;
mode->vrefresh = drm_mode_vrefresh(mode);
- drm_mode_probed_add(connector, mode);
+ if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2,
+ mode->vdisplay)) {
+ drm_mode_probed_add(connector, mode);
- if (ldu->pref_mode) {
- list_del_init(&ldu->pref_mode->head);
- drm_mode_destroy(dev, ldu->pref_mode);
- }
+ if (ldu->pref_mode) {
+ list_del_init(&ldu->pref_mode->head);
+ drm_mode_destroy(dev, ldu->pref_mode);
+ }
- ldu->pref_mode = mode;
+ ldu->pref_mode = mode;
+ }
}
for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) {
- if (vmw_ldu_connector_builtin[i].hdisplay > max_width ||
- vmw_ldu_connector_builtin[i].vdisplay > max_height)
+ bmode = &vmw_ldu_connector_builtin[i];
+ if (bmode->hdisplay > max_width ||
+ bmode->vdisplay > max_height)
+ continue;
+
+ if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2,
+ bmode->vdisplay))
continue;
- mode = drm_mode_duplicate(dev, &vmw_ldu_connector_builtin[i]);
+ mode = drm_mode_duplicate(dev, bmode);
if (!mode)
return 0;
mode->vrefresh = drm_mode_vrefresh(mode);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index c8c40e9979db..36e129f0023f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -765,28 +765,11 @@ static size_t vmw_dmabuf_acc_size(struct ttm_bo_global *glob,
return bo_user_size + page_array_size;
}
-void vmw_dmabuf_gmr_unbind(struct ttm_buffer_object *bo)
-{
- struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
- struct ttm_bo_global *glob = bo->glob;
- struct vmw_private *dev_priv =
- container_of(bo->bdev, struct vmw_private, bdev);
-
- if (vmw_bo->gmr_bound) {
- vmw_gmr_unbind(dev_priv, vmw_bo->gmr_id);
- spin_lock(&glob->lru_lock);
- ida_remove(&dev_priv->gmr_ida, vmw_bo->gmr_id);
- spin_unlock(&glob->lru_lock);
- vmw_bo->gmr_bound = false;
- }
-}
-
void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo)
{
struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
struct ttm_bo_global *glob = bo->glob;
- vmw_dmabuf_gmr_unbind(bo);
ttm_mem_global_free(glob->mem_glob, bo->acc_size);
kfree(vmw_bo);
}
@@ -818,10 +801,7 @@ int vmw_dmabuf_init(struct vmw_private *dev_priv,
memset(vmw_bo, 0, sizeof(*vmw_bo));
- INIT_LIST_HEAD(&vmw_bo->gmr_lru);
INIT_LIST_HEAD(&vmw_bo->validate_list);
- vmw_bo->gmr_id = 0;
- vmw_bo->gmr_bound = false;
ret = ttm_bo_init(bdev, &vmw_bo->base, size,
ttm_bo_type_device, placement,
@@ -835,7 +815,6 @@ static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo)
struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo);
struct ttm_bo_global *glob = bo->glob;
- vmw_dmabuf_gmr_unbind(bo);
ttm_mem_global_free(glob->mem_glob, bo->acc_size);
kfree(vmw_user_bo);
}
@@ -938,25 +917,6 @@ void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo)
vmw_bo->on_validate_list = false;
}
-uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo)
-{
- struct vmw_dma_buffer *vmw_bo;
-
- if (bo->mem.mem_type == TTM_PL_VRAM)
- return SVGA_GMR_FRAMEBUFFER;
-
- vmw_bo = vmw_dma_buffer(bo);
-
- return (vmw_bo->gmr_bound) ? vmw_bo->gmr_id : SVGA_GMR_NULL;
-}
-
-void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id)
-{
- struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
- vmw_bo->gmr_bound = true;
- vmw_bo->gmr_id = id;
-}
-
int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
uint32_t handle, struct vmw_dma_buffer **out)
{
@@ -985,41 +945,6 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
return 0;
}
-/**
- * TODO: Implement a gmr id eviction mechanism. Currently we just fail
- * when we're out of ids, causing GMR space to be allocated
- * out of VRAM.
- */
-
-int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id)
-{
- struct ttm_bo_global *glob = dev_priv->bdev.glob;
- int id;
- int ret;
-
- do {
- if (unlikely(ida_pre_get(&dev_priv->gmr_ida, GFP_KERNEL) == 0))
- return -ENOMEM;
-
- spin_lock(&glob->lru_lock);
- ret = ida_get_new(&dev_priv->gmr_ida, &id);
- spin_unlock(&glob->lru_lock);
- } while (ret == -EAGAIN);
-
- if (unlikely(ret != 0))
- return ret;
-
- if (unlikely(id >= dev_priv->max_gmr_ids)) {
- spin_lock(&glob->lru_lock);
- ida_remove(&dev_priv->gmr_ida, id);
- spin_unlock(&glob->lru_lock);
- return -EBUSY;
- }
-
- *p_id = (uint32_t) id;
- return 0;
-}
-
/*
* Stream management
*/
diff --git a/drivers/gpu/stub/Kconfig b/drivers/gpu/stub/Kconfig
new file mode 100644
index 000000000000..742c423567cf
--- /dev/null
+++ b/drivers/gpu/stub/Kconfig
@@ -0,0 +1,13 @@
+config STUB_POULSBO
+ tristate "Intel GMA500 Stub Driver"
+ depends on PCI
+ # Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled
+ # but for select to work, need to select ACPI_VIDEO's dependencies, ick
+ select ACPI_VIDEO if ACPI
+ help
+ Choose this option if you have a system that has Intel GMA500
+ (Poulsbo) integrated graphics. If M is selected, the module will
+ be called Poulsbo. This driver is a stub driver for Poulsbo that
+ will call poulsbo.ko to enable the acpi backlight control sysfs
+ entry file because there have no poulsbo native driver can support
+ intel opregion.
diff --git a/drivers/gpu/stub/Makefile b/drivers/gpu/stub/Makefile
new file mode 100644
index 000000000000..cd940cc9d36d
--- /dev/null
+++ b/drivers/gpu/stub/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_STUB_POULSBO) += poulsbo.o
diff --git a/drivers/gpu/stub/poulsbo.c b/drivers/gpu/stub/poulsbo.c
new file mode 100644
index 000000000000..7edfd27b8dee
--- /dev/null
+++ b/drivers/gpu/stub/poulsbo.c
@@ -0,0 +1,64 @@
+/*
+ * Intel Poulsbo Stub driver
+ *
+ * Copyright (C) 2010 Novell <jlee@novell.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/pci.h>
+#include <linux/acpi.h>
+#include <acpi/video.h>
+
+#define DRIVER_NAME "poulsbo"
+
+enum {
+ CHIP_PSB_8108 = 0,
+ CHIP_PSB_8109 = 1,
+};
+
+static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
+ {0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8108}, \
+ {0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8109}, \
+ {0, 0, 0}
+};
+
+static int poulsbo_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ return acpi_video_register();
+}
+
+static void poulsbo_remove(struct pci_dev *pdev)
+{
+ acpi_video_unregister();
+}
+
+static struct pci_driver poulsbo_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pciidlist,
+ .probe = poulsbo_probe,
+ .remove = poulsbo_remove,
+};
+
+static int __init poulsbo_init(void)
+{
+ return pci_register_driver(&poulsbo_driver);
+}
+
+static void __exit poulsbo_exit(void)
+{
+ pci_unregister_driver(&poulsbo_driver);
+}
+
+module_init(poulsbo_init);
+module_exit(poulsbo_exit);
+
+MODULE_AUTHOR("Lee, Chun-Yi <jlee@novell.com>");
+MODULE_DESCRIPTION("Poulsbo Stub Driver");
+MODULE_LICENSE("GPL");
+
+MODULE_DEVICE_TABLE(pci, pciidlist);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 7832b6e2478b..515345b11ac9 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1780,6 +1780,11 @@ static bool hid_ignore(struct hid_device *hdev)
hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST)
return true;
break;
+ case USB_VENDOR_ID_HANWANG:
+ if (hdev->product >= USB_DEVICE_ID_HANWANG_TABLET_FIRST &&
+ hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST)
+ return true;
+ break;
}
if (hdev->type == HID_TYPE_USBMOUSE &&
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 3ee999d33004..3341baa86a30 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -299,6 +299,10 @@
#define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003
#define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008
+#define USB_VENDOR_ID_HANWANG 0x0b57
+#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
+#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff
+
#define USB_VENDOR_ID_HAPP 0x078b
#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 834ef47b76d6..bb0b3659437b 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -68,39 +68,52 @@ static const struct {
#define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \
&max, EV_KEY, (c))
-static inline int match_scancode(unsigned int code, unsigned int scancode)
+static bool match_scancode(struct hid_usage *usage,
+ unsigned int cur_idx, unsigned int scancode)
{
- if (scancode == 0)
- return 1;
-
- return (code & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
+ return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
}
-static inline int match_keycode(unsigned int code, unsigned int keycode)
+static bool match_keycode(struct hid_usage *usage,
+ unsigned int cur_idx, unsigned int keycode)
{
- if (keycode == 0)
- return 1;
+ /*
+ * We should exclude unmapped usages when doing lookup by keycode.
+ */
+ return (usage->type == EV_KEY && usage->code == keycode);
+}
- return code == keycode;
+static bool match_index(struct hid_usage *usage,
+ unsigned int cur_idx, unsigned int idx)
+{
+ return cur_idx == idx;
}
+typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage,
+ unsigned int cur_idx, unsigned int val);
+
static struct hid_usage *hidinput_find_key(struct hid_device *hid,
- unsigned int scancode,
- unsigned int keycode)
+ hid_usage_cmp_t match,
+ unsigned int value,
+ unsigned int *usage_idx)
{
- int i, j, k;
+ unsigned int i, j, k, cur_idx = 0;
struct hid_report *report;
struct hid_usage *usage;
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
for (i = 0; i < report->maxfield; i++) {
- for ( j = 0; j < report->field[i]->maxusage; j++) {
+ for (j = 0; j < report->field[i]->maxusage; j++) {
usage = report->field[i]->usage + j;
- if (usage->type == EV_KEY &&
- match_scancode(usage->hid, scancode) &&
- match_keycode(usage->code, keycode))
- return usage;
+ if (usage->type == EV_KEY || usage->type == 0) {
+ if (match(usage, cur_idx, value)) {
+ if (usage_idx)
+ *usage_idx = cur_idx;
+ return usage;
+ }
+ cur_idx++;
+ }
}
}
}
@@ -108,39 +121,68 @@ static struct hid_usage *hidinput_find_key(struct hid_device *hid,
return NULL;
}
+static struct hid_usage *hidinput_locate_usage(struct hid_device *hid,
+ const struct input_keymap_entry *ke,
+ unsigned int *index)
+{
+ struct hid_usage *usage;
+ unsigned int scancode;
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+ usage = hidinput_find_key(hid, match_index, ke->index, index);
+ else if (input_scancode_to_scalar(ke, &scancode) == 0)
+ usage = hidinput_find_key(hid, match_scancode, scancode, index);
+ else
+ usage = NULL;
+
+ return usage;
+}
+
static int hidinput_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
+ struct input_keymap_entry *ke)
{
struct hid_device *hid = input_get_drvdata(dev);
struct hid_usage *usage;
+ unsigned int scancode, index;
- usage = hidinput_find_key(hid, scancode, 0);
+ usage = hidinput_locate_usage(hid, ke, &index);
if (usage) {
- *keycode = usage->code;
+ ke->keycode = usage->type == EV_KEY ?
+ usage->code : KEY_RESERVED;
+ ke->index = index;
+ scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE);
+ ke->len = sizeof(scancode);
+ memcpy(ke->scancode, &scancode, sizeof(scancode));
return 0;
}
+
return -EINVAL;
}
static int hidinput_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
{
struct hid_device *hid = input_get_drvdata(dev);
struct hid_usage *usage;
- int old_keycode;
- usage = hidinput_find_key(hid, scancode, 0);
+ usage = hidinput_locate_usage(hid, ke, NULL);
if (usage) {
- old_keycode = usage->code;
- usage->code = keycode;
+ *old_keycode = usage->type == EV_KEY ?
+ usage->code : KEY_RESERVED;
+ usage->code = ke->keycode;
- clear_bit(old_keycode, dev->keybit);
+ clear_bit(*old_keycode, dev->keybit);
set_bit(usage->code, dev->keybit);
- dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
- /* Set the keybit for the old keycode if the old keycode is used
- * by another key */
- if (hidinput_find_key (hid, 0, old_keycode))
- set_bit(old_keycode, dev->keybit);
+ dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n",
+ usage->code, usage->hid);
+
+ /*
+ * Set the keybit for the old keycode if the old keycode is used
+ * by another key
+ */
+ if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL))
+ set_bit(*old_keycode, dev->keybit);
return 0;
}
@@ -835,8 +877,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
hid->ll_driver->hidinput_input_event;
input_dev->open = hidinput_open;
input_dev->close = hidinput_close;
- input_dev->setkeycode = hidinput_setkeycode;
- input_dev->getkeycode = hidinput_getkeycode;
+ input_dev->setkeycode_new = hidinput_setkeycode;
+ input_dev->getkeycode_new = hidinput_getkeycode;
input_dev->name = hid->name;
input_dev->phys = hid->phys;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 97499d00615a..c357c835eb1e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -399,6 +399,15 @@ config SENSORS_GL520SM
This driver can also be built as a module. If so, the module
will be called gl520sm.
+config SENSORS_GPIO_FAN
+ tristate "GPIO fan"
+ depends on GENERIC_GPIO
+ help
+ If you say yes here you get support for fans connected to GPIO lines.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-fan.
+
config SENSORS_CORETEMP
tristate "Intel Core/Core2/Atom temperature sensor"
depends on X86 && PCI && EXPERIMENTAL
@@ -654,6 +663,17 @@ config SENSORS_LTC4245
This driver can also be built as a module. If so, the module will
be called ltc4245.
+config SENSORS_LTC4261
+ tristate "Linear Technology LTC4261"
+ depends on I2C && EXPERIMENTAL
+ default n
+ help
+ If you say yes here you get support for Linear Technology LTC4261
+ Negative Voltage Hot Swap Controller I2C interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc4261.
+
config SENSORS_LM95241
tristate "National Semiconductor LM95241 sensor chip"
depends on I2C
@@ -1088,26 +1108,6 @@ config SENSORS_ULTRA45
This driver provides support for the Ultra45 workstation environmental
sensors.
-config SENSORS_HDAPS
- tristate "IBM Hard Drive Active Protection System (hdaps)"
- depends on INPUT && X86
- select INPUT_POLLDEV
- default n
- help
- This driver provides support for the IBM Hard Drive Active Protection
- System (hdaps), which provides an accelerometer and other misc. data.
- ThinkPads starting with the R50, T41, and X40 are supported. The
- accelerometer data is readable via sysfs.
-
- This driver also provides an absolute input class device, allowing
- the laptop to act as a pinball machine-esque joystick.
-
- If your ThinkPad is not recognized by the driver, please update to latest
- BIOS. This is especially the case for some R52 ThinkPads.
-
- Say Y here if you have an applicable laptop and want to experience
- the awesome power of hdaps.
-
config SENSORS_LIS3_SPI
tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)"
depends on !ACPI && SPI_MASTER && INPUT
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e3c2484f6c5f..d30f0f6870e0 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -51,8 +51,8 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o
obj-$(CONFIG_SENSORS_G760A) += g760a.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
+obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
-obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
@@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
+obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index a23b17a78ace..42de98d73ff5 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -21,7 +21,6 @@
*/
#include <linux/module.h>
-#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
@@ -280,11 +279,9 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
case 0x1a:
dev_warn(dev, "TjMax is assumed as 100 C!\n");
return 100000;
- break;
case 0x17:
case 0x1c: /* Atom CPUs */
return adjust_tjmax(c, id, dev);
- break;
default:
dev_warn(dev, "CPU (model=0x%x) is not supported yet,"
" using default TjMax of 100C.\n", c->x86_model);
@@ -292,6 +289,15 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
}
}
+static void __devinit get_ucode_rev_on_cpu(void *edx)
+{
+ u32 eax;
+
+ wrmsr(MSR_IA32_UCODE_REV, 0, 0);
+ sync_core();
+ rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx);
+}
+
static int __devinit coretemp_probe(struct platform_device *pdev)
{
struct coretemp_data *data;
@@ -327,8 +333,15 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) {
/* check for microcode update */
- rdmsr_on_cpu(data->id, MSR_IA32_UCODE_REV, &eax, &edx);
- if (edx < 0x39) {
+ err = smp_call_function_single(data->id, get_ucode_rev_on_cpu,
+ &edx, 1);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Cannot determine microcode revision of "
+ "CPU#%u (%d)!\n", data->id, err);
+ err = -ENODEV;
+ goto exit_free;
+ } else if (edx < 0x39) {
err = -ENODEV;
dev_err(&pdev->dev,
"Errata AE18 not fixed, update BIOS or "
@@ -490,7 +503,7 @@ exit:
return err;
}
-static void coretemp_device_remove(unsigned int cpu)
+static void __cpuinit coretemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p;
unsigned int i;
@@ -569,9 +582,8 @@ exit:
static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
-#ifdef CONFIG_HOTPLUG_CPU
+
unregister_hotcpu_notifier(&coretemp_cpu_notifier);
-#endif
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
new file mode 100644
index 000000000000..aa701a183707
--- /dev/null
+++ b/drivers/hwmon/gpio-fan.c
@@ -0,0 +1,558 @@
+/*
+ * gpio-fan.c - Hwmon driver for fans connected to GPIO lines.
+ *
+ * Copyright (C) 2010 LaCie
+ *
+ * Author: Simon Guinot <sguinot@lacie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/gpio.h>
+#include <linux/gpio-fan.h>
+
+struct gpio_fan_data {
+ struct platform_device *pdev;
+ struct device *hwmon_dev;
+ struct mutex lock; /* lock GPIOs operations. */
+ int num_ctrl;
+ unsigned *ctrl;
+ int num_speed;
+ struct gpio_fan_speed *speed;
+ int speed_index;
+#ifdef CONFIG_PM
+ int resume_speed;
+#endif
+ bool pwm_enable;
+ struct gpio_fan_alarm *alarm;
+ struct work_struct alarm_work;
+};
+
+/*
+ * Alarm GPIO.
+ */
+
+static void fan_alarm_notify(struct work_struct *ws)
+{
+ struct gpio_fan_data *fan_data =
+ container_of(ws, struct gpio_fan_data, alarm_work);
+
+ sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm");
+ kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE);
+}
+
+static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_fan_data *fan_data = dev_id;
+
+ schedule_work(&fan_data->alarm_work);
+
+ return IRQ_NONE;
+}
+
+static ssize_t show_fan_alarm(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ struct gpio_fan_alarm *alarm = fan_data->alarm;
+ int value = gpio_get_value(alarm->gpio);
+
+ if (alarm->active_low)
+ value = !value;
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL);
+
+static int fan_alarm_init(struct gpio_fan_data *fan_data,
+ struct gpio_fan_alarm *alarm)
+{
+ int err;
+ int alarm_irq;
+ struct platform_device *pdev = fan_data->pdev;
+
+ fan_data->alarm = alarm;
+
+ err = gpio_request(alarm->gpio, "GPIO fan alarm");
+ if (err)
+ return err;
+
+ err = gpio_direction_input(alarm->gpio);
+ if (err)
+ goto err_free_gpio;
+
+ err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm);
+ if (err)
+ goto err_free_gpio;
+
+ /*
+ * If the alarm GPIO don't support interrupts, just leave
+ * without initializing the fail notification support.
+ */
+ alarm_irq = gpio_to_irq(alarm->gpio);
+ if (alarm_irq < 0)
+ return 0;
+
+ INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
+ set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
+ err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED,
+ "GPIO fan alarm", fan_data);
+ if (err)
+ goto err_free_sysfs;
+
+ return 0;
+
+err_free_sysfs:
+ device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
+err_free_gpio:
+ gpio_free(alarm->gpio);
+
+ return err;
+}
+
+static void fan_alarm_free(struct gpio_fan_data *fan_data)
+{
+ struct platform_device *pdev = fan_data->pdev;
+ int alarm_irq = gpio_to_irq(fan_data->alarm->gpio);
+
+ if (alarm_irq >= 0)
+ free_irq(alarm_irq, fan_data);
+ device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
+ gpio_free(fan_data->alarm->gpio);
+}
+
+/*
+ * Control GPIOs.
+ */
+
+/* Must be called with fan_data->lock held, except during initialization. */
+static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
+{
+ int i;
+
+ for (i = 0; i < fan_data->num_ctrl; i++)
+ gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1);
+}
+
+static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
+{
+ int i;
+ int ctrl_val = 0;
+
+ for (i = 0; i < fan_data->num_ctrl; i++) {
+ int value;
+
+ value = gpio_get_value(fan_data->ctrl[i]);
+ ctrl_val |= (value << i);
+ }
+ return ctrl_val;
+}
+
+/* Must be called with fan_data->lock held, except during initialization. */
+static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
+{
+ if (fan_data->speed_index == speed_index)
+ return;
+
+ __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
+ fan_data->speed_index = speed_index;
+}
+
+static int get_fan_speed_index(struct gpio_fan_data *fan_data)
+{
+ int ctrl_val = __get_fan_ctrl(fan_data);
+ int i;
+
+ for (i = 0; i < fan_data->num_speed; i++)
+ if (fan_data->speed[i].ctrl_val == ctrl_val)
+ return i;
+
+ dev_warn(&fan_data->pdev->dev,
+ "missing speed array entry for GPIO value 0x%x\n", ctrl_val);
+
+ return -EINVAL;
+}
+
+static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm)
+{
+ struct gpio_fan_speed *speed = fan_data->speed;
+ int i;
+
+ for (i = 0; i < fan_data->num_speed; i++)
+ if (speed[i].rpm >= rpm)
+ return i;
+
+ return fan_data->num_speed - 1;
+}
+
+static ssize_t show_pwm(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1);
+
+ return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int speed_index;
+ int ret = count;
+
+ if (strict_strtoul(buf, 10, &pwm) || pwm > 255)
+ return -EINVAL;
+
+ mutex_lock(&fan_data->lock);
+
+ if (!fan_data->pwm_enable) {
+ ret = -EPERM;
+ goto exit_unlock;
+ }
+
+ speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
+ set_fan_speed(fan_data, speed_index);
+
+exit_unlock:
+ mutex_unlock(&fan_data->lock);
+
+ return ret;
+}
+
+static ssize_t show_pwm_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", fan_data->pwm_enable);
+}
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) || val > 1)
+ return -EINVAL;
+
+ if (fan_data->pwm_enable == val)
+ return count;
+
+ mutex_lock(&fan_data->lock);
+
+ fan_data->pwm_enable = val;
+
+ /* Disable manual control mode: set fan at full speed. */
+ if (val == 0)
+ set_fan_speed(fan_data, fan_data->num_speed - 1);
+
+ mutex_unlock(&fan_data->lock);
+
+ return count;
+}
+
+static ssize_t show_pwm_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t show_rpm_min(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", fan_data->speed[0].rpm);
+}
+
+static ssize_t show_rpm_max(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n",
+ fan_data->speed[fan_data->num_speed - 1].rpm);
+}
+
+static ssize_t show_rpm(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm);
+}
+
+static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ unsigned long rpm;
+ int ret = count;
+
+ if (strict_strtoul(buf, 10, &rpm))
+ return -EINVAL;
+
+ mutex_lock(&fan_data->lock);
+
+ if (!fan_data->pwm_enable) {
+ ret = -EPERM;
+ goto exit_unlock;
+ }
+
+ set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
+
+exit_unlock:
+ mutex_unlock(&fan_data->lock);
+
+ return ret;
+}
+
+static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
+static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable);
+static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL);
+static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL);
+static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL);
+static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL);
+static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm);
+
+static struct attribute *gpio_fan_ctrl_attributes[] = {
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm1_mode.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_target.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_max.attr,
+ NULL
+};
+
+static const struct attribute_group gpio_fan_ctrl_group = {
+ .attrs = gpio_fan_ctrl_attributes,
+};
+
+static int fan_ctrl_init(struct gpio_fan_data *fan_data,
+ struct gpio_fan_platform_data *pdata)
+{
+ struct platform_device *pdev = fan_data->pdev;
+ int num_ctrl = pdata->num_ctrl;
+ unsigned *ctrl = pdata->ctrl;
+ int i, err;
+
+ for (i = 0; i < num_ctrl; i++) {
+ err = gpio_request(ctrl[i], "GPIO fan control");
+ if (err)
+ goto err_free_gpio;
+
+ err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i]));
+ if (err) {
+ gpio_free(ctrl[i]);
+ goto err_free_gpio;
+ }
+ }
+
+ err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
+ if (err)
+ goto err_free_gpio;
+
+ fan_data->num_ctrl = num_ctrl;
+ fan_data->ctrl = ctrl;
+ fan_data->num_speed = pdata->num_speed;
+ fan_data->speed = pdata->speed;
+ fan_data->pwm_enable = true; /* Enable manual fan speed control. */
+ fan_data->speed_index = get_fan_speed_index(fan_data);
+ if (fan_data->speed_index < 0) {
+ err = -ENODEV;
+ goto err_free_gpio;
+ }
+
+ return 0;
+
+err_free_gpio:
+ for (i = i - 1; i >= 0; i--)
+ gpio_free(ctrl[i]);
+
+ return err;
+}
+
+static void fan_ctrl_free(struct gpio_fan_data *fan_data)
+{
+ struct platform_device *pdev = fan_data->pdev;
+ int i;
+
+ sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
+ for (i = 0; i < fan_data->num_ctrl; i++)
+ gpio_free(fan_data->ctrl[i]);
+}
+
+/*
+ * Platform driver.
+ */
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "gpio-fan\n");
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static int __devinit gpio_fan_probe(struct platform_device *pdev)
+{
+ int err;
+ struct gpio_fan_data *fan_data;
+ struct gpio_fan_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL);
+ if (!fan_data)
+ return -ENOMEM;
+
+ fan_data->pdev = pdev;
+ platform_set_drvdata(pdev, fan_data);
+ mutex_init(&fan_data->lock);
+
+ /* Configure alarm GPIO if available. */
+ if (pdata->alarm) {
+ err = fan_alarm_init(fan_data, pdata->alarm);
+ if (err)
+ goto err_free_data;
+ }
+
+ /* Configure control GPIOs if available. */
+ if (pdata->ctrl && pdata->num_ctrl > 0) {
+ if (!pdata->speed || pdata->num_speed <= 1) {
+ err = -EINVAL;
+ goto err_free_alarm;
+ }
+ err = fan_ctrl_init(fan_data, pdata);
+ if (err)
+ goto err_free_alarm;
+ }
+
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
+ goto err_free_ctrl;
+
+ /* Make this driver part of hwmon class. */
+ fan_data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(fan_data->hwmon_dev)) {
+ err = PTR_ERR(fan_data->hwmon_dev);
+ goto err_remove_name;
+ }
+
+ dev_info(&pdev->dev, "GPIO fan initialized\n");
+
+ return 0;
+
+err_remove_name:
+ device_remove_file(&pdev->dev, &dev_attr_name);
+err_free_ctrl:
+ if (fan_data->ctrl)
+ fan_ctrl_free(fan_data);
+err_free_alarm:
+ if (fan_data->alarm)
+ fan_alarm_free(fan_data);
+err_free_data:
+ platform_set_drvdata(pdev, NULL);
+ kfree(fan_data);
+
+ return err;
+}
+
+static int __devexit gpio_fan_remove(struct platform_device *pdev)
+{
+ struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(fan_data->hwmon_dev);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ if (fan_data->alarm)
+ fan_alarm_free(fan_data);
+ if (fan_data->ctrl)
+ fan_ctrl_free(fan_data);
+ kfree(fan_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
+
+ if (fan_data->ctrl) {
+ fan_data->resume_speed = fan_data->speed_index;
+ set_fan_speed(fan_data, 0);
+ }
+
+ return 0;
+}
+
+static int gpio_fan_resume(struct platform_device *pdev)
+{
+ struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
+
+ if (fan_data->ctrl)
+ set_fan_speed(fan_data, fan_data->resume_speed);
+
+ return 0;
+}
+#else
+#define gpio_fan_suspend NULL
+#define gpio_fan_resume NULL
+#endif
+
+static struct platform_driver gpio_fan_driver = {
+ .probe = gpio_fan_probe,
+ .remove = __devexit_p(gpio_fan_remove),
+ .suspend = gpio_fan_suspend,
+ .resume = gpio_fan_resume,
+ .driver = {
+ .name = "gpio-fan",
+ },
+};
+
+static int __init gpio_fan_init(void)
+{
+ return platform_driver_register(&gpio_fan_driver);
+}
+
+static void __exit gpio_fan_exit(void)
+{
+ platform_driver_unregister(&gpio_fan_driver);
+}
+
+module_init(gpio_fan_init);
+module_exit(gpio_fan_exit);
+
+MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
+MODULE_DESCRIPTION("GPIO FAN driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-fan");
diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c
index 36e957532230..a56a78412fcb 100644
--- a/drivers/hwmon/hp_accel.c
+++ b/drivers/hwmon/hp_accel.c
@@ -146,7 +146,7 @@ int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val)
static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi)
{
- lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data);
+ lis3_dev.ac = *((union axis_conversion *)dmi->driver_data);
printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident);
return 1;
@@ -154,16 +154,19 @@ static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi)
/* Represents, for each axis seen by userspace, the corresponding hw axis (+1).
* If the value is negative, the opposite of the hw value is used. */
-static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3};
-static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3};
-static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3};
-static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3};
-static struct axis_conversion lis3lv02d_axis_xy_swap = {2, 1, 3};
-static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3};
-static struct axis_conversion lis3lv02d_axis_xy_rotated_left_usd = {-2, 1, -3};
-static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3};
-static struct axis_conversion lis3lv02d_axis_xy_rotated_right = {2, -1, 3};
-static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3};
+#define DEFINE_CONV(name, x, y, z) \
+ static union axis_conversion lis3lv02d_axis_##name = \
+ { .as_array = { x, y, z } }
+DEFINE_CONV(normal, 1, 2, 3);
+DEFINE_CONV(y_inverted, 1, -2, 3);
+DEFINE_CONV(x_inverted, -1, 2, 3);
+DEFINE_CONV(z_inverted, 1, 2, -3);
+DEFINE_CONV(xy_swap, 2, 1, 3);
+DEFINE_CONV(xy_rotated_left, -2, 1, 3);
+DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3);
+DEFINE_CONV(xy_swap_inverted, -2, -1, 3);
+DEFINE_CONV(xy_rotated_right, 2, -1, 3);
+DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3);
#define AXIS_DMI_MATCH(_ident, _name, _axis) { \
.ident = _ident, \
@@ -222,7 +225,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),
- AXIS_DMI_MATCH("Mini5102", "HP Mini 5102", xy_rotated_left_usd),
+ AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd),
{ NULL, }
/* Laptop models without axis info (yet):
* "NC6910" "HP Compaq 6910"
@@ -299,7 +302,10 @@ static int lis3lv02d_add(struct acpi_device *device)
lis3lv02d_enum_resources(device);
/* If possible use a "standard" axes order */
- if (dmi_check_system(lis3lv02d_dmi_ids) == 0) {
+ if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) {
+ printk(KERN_INFO DRIVER_NAME ": Using custom axes %d,%d,%d\n",
+ lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z);
+ } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) {
printk(KERN_INFO DRIVER_NAME ": laptop model unknown, "
"using default axes configuration\n");
lis3_dev.ac = lis3lv02d_axis_normal;
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c
index fc591ae53107..0cee73a6124e 100644
--- a/drivers/hwmon/lis3lv02d.c
+++ b/drivers/hwmon/lis3lv02d.c
@@ -31,9 +31,11 @@
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/poll.h>
+#include <linux/slab.h>
#include <linux/freezer.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
+#include <linux/pm_runtime.h>
#include <asm/atomic.h>
#include "lis3lv02d.h"
@@ -43,6 +45,16 @@
#define MDPS_POLL_INTERVAL 50
#define MDPS_POLL_MIN 0
#define MDPS_POLL_MAX 2000
+
+#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */
+
+#define SELFTEST_OK 0
+#define SELFTEST_FAIL -1
+#define SELFTEST_IRQ -2
+
+#define IRQ_LINE0 0
+#define IRQ_LINE1 1
+
/*
* The sensor can also generate interrupts (DRDY) but it's pretty pointless
* because they are generated even if the data do not change. So it's better
@@ -66,8 +78,10 @@
#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)
#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)
-#define LIS3_DEFAULT_FUZZ 3
-#define LIS3_DEFAULT_FLAT 3
+#define LIS3_DEFAULT_FUZZ_12B 3
+#define LIS3_DEFAULT_FLAT_12B 3
+#define LIS3_DEFAULT_FUZZ_8B 1
+#define LIS3_DEFAULT_FLAT_8B 1
struct lis3lv02d lis3_dev = {
.misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait),
@@ -75,6 +89,30 @@ struct lis3lv02d lis3_dev = {
EXPORT_SYMBOL_GPL(lis3_dev);
+/* just like param_set_int() but does sanity-check so that it won't point
+ * over the axis array size
+ */
+static int param_set_axis(const char *val, const struct kernel_param *kp)
+{
+ int ret = param_set_int(val, kp);
+ if (!ret) {
+ int val = *(int *)kp->arg;
+ if (val < 0)
+ val = -val;
+ if (!val || val > 3)
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static struct kernel_param_ops param_ops_axis = {
+ .set = param_set_axis,
+ .get = param_get_int,
+};
+
+module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644);
+MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions");
+
static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg)
{
s8 lo;
@@ -123,9 +161,24 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
int position[3];
int i;
- position[0] = lis3->read_data(lis3, OUTX);
- position[1] = lis3->read_data(lis3, OUTY);
- position[2] = lis3->read_data(lis3, OUTZ);
+ if (lis3->blkread) {
+ if (lis3_dev.whoami == WAI_12B) {
+ u16 data[3];
+ lis3->blkread(lis3, OUTX_L, 6, (u8 *)data);
+ for (i = 0; i < 3; i++)
+ position[i] = (s16)le16_to_cpu(data[i]);
+ } else {
+ u8 data[5];
+ /* Data: x, dummy, y, dummy, z */
+ lis3->blkread(lis3, OUTX, 5, data);
+ for (i = 0; i < 3; i++)
+ position[i] = (s8)data[i * 2];
+ }
+ } else {
+ position[0] = lis3->read_data(lis3, OUTX);
+ position[1] = lis3->read_data(lis3, OUTY);
+ position[2] = lis3->read_data(lis3, OUTZ);
+ }
for (i = 0; i < 3; i++)
position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY;
@@ -138,6 +191,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
/* conversion btw sampling rate and the register values */
static int lis3_12_rates[4] = {40, 160, 640, 2560};
static int lis3_8_rates[2] = {100, 400};
+static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
/* ODR is Output Data Rate */
static int lis3lv02d_get_odr(void)
@@ -156,6 +210,9 @@ static int lis3lv02d_set_odr(int rate)
u8 ctrl;
int i, len, shift;
+ if (!rate)
+ return -EINVAL;
+
lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
ctrl &= ~lis3_dev.odr_mask;
len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */
@@ -172,19 +229,42 @@ static int lis3lv02d_set_odr(int rate)
static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
{
- u8 reg;
+ u8 ctlreg, reg;
s16 x, y, z;
u8 selftest;
int ret;
+ u8 ctrl_reg_data;
+ unsigned char irq_cfg;
mutex_lock(&lis3->mutex);
- if (lis3_dev.whoami == WAI_12B)
- selftest = CTRL1_ST;
- else
- selftest = CTRL1_STP;
- lis3->read(lis3, CTRL_REG1, &reg);
- lis3->write(lis3, CTRL_REG1, (reg | selftest));
+ irq_cfg = lis3->irq_cfg;
+ if (lis3_dev.whoami == WAI_8B) {
+ lis3->data_ready_count[IRQ_LINE0] = 0;
+ lis3->data_ready_count[IRQ_LINE1] = 0;
+
+ /* Change interrupt cfg to data ready for selftest */
+ atomic_inc(&lis3_dev.wake_thread);
+ lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY;
+ lis3->read(lis3, CTRL_REG3, &ctrl_reg_data);
+ lis3->write(lis3, CTRL_REG3, (ctrl_reg_data &
+ ~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) |
+ (LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
+ }
+
+ if (lis3_dev.whoami == WAI_3DC) {
+ ctlreg = CTRL_REG4;
+ selftest = CTRL4_ST0;
+ } else {
+ ctlreg = CTRL_REG1;
+ if (lis3_dev.whoami == WAI_12B)
+ selftest = CTRL1_ST;
+ else
+ selftest = CTRL1_STP;
+ }
+
+ lis3->read(lis3, ctlreg, &reg);
+ lis3->write(lis3, ctlreg, (reg | selftest));
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
/* Read directly to avoid axis remap */
@@ -193,7 +273,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
z = lis3->read_data(lis3, OUTZ);
/* back to normal settings */
- lis3->write(lis3, CTRL_REG1, reg);
+ lis3->write(lis3, ctlreg, reg);
msleep(lis3->pwron_delay / lis3lv02d_get_odr());
results[0] = x - lis3->read_data(lis3, OUTX);
@@ -201,13 +281,33 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
results[2] = z - lis3->read_data(lis3, OUTZ);
ret = 0;
+
+ if (lis3_dev.whoami == WAI_8B) {
+ /* Restore original interrupt configuration */
+ atomic_dec(&lis3_dev.wake_thread);
+ lis3->write(lis3, CTRL_REG3, ctrl_reg_data);
+ lis3->irq_cfg = irq_cfg;
+
+ if ((irq_cfg & LIS3_IRQ1_MASK) &&
+ lis3->data_ready_count[IRQ_LINE0] < 2) {
+ ret = SELFTEST_IRQ;
+ goto fail;
+ }
+
+ if ((irq_cfg & LIS3_IRQ2_MASK) &&
+ lis3->data_ready_count[IRQ_LINE1] < 2) {
+ ret = SELFTEST_IRQ;
+ goto fail;
+ }
+ }
+
if (lis3->pdata) {
int i;
for (i = 0; i < 3; i++) {
/* Check against selftest acceptance limits */
if ((results[i] < lis3->pdata->st_min_limits[i]) ||
(results[i] > lis3->pdata->st_max_limits[i])) {
- ret = -EIO;
+ ret = SELFTEST_FAIL;
goto fail;
}
}
@@ -219,10 +319,46 @@ fail:
return ret;
}
+/*
+ * Order of registers in the list affects to order of the restore process.
+ * Perhaps it is a good idea to set interrupt enable register as a last one
+ * after all other configurations
+ */
+static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1,
+ FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2,
+ CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ,
+ CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW,
+ CTRL_REG1, CTRL_REG2, CTRL_REG3};
+
+static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H,
+ FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H,
+ DD_THSE_L, DD_THSE_H,
+ CTRL_REG1, CTRL_REG3, CTRL_REG2};
+
+static inline void lis3_context_save(struct lis3lv02d *lis3)
+{
+ int i;
+ for (i = 0; i < lis3->regs_size; i++)
+ lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]);
+ lis3->regs_stored = true;
+}
+
+static inline void lis3_context_restore(struct lis3lv02d *lis3)
+{
+ int i;
+ if (lis3->regs_stored)
+ for (i = 0; i < lis3->regs_size; i++)
+ lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]);
+}
+
void lis3lv02d_poweroff(struct lis3lv02d *lis3)
{
+ if (lis3->reg_ctrl)
+ lis3_context_save(lis3);
/* disable X,Y,Z axis and power down */
lis3->write(lis3, CTRL_REG1, 0x00);
+ if (lis3->reg_ctrl)
+ lis3->reg_ctrl(lis3, LIS3_REG_OFF);
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
@@ -232,19 +368,24 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
lis3->init(lis3);
- /* LIS3 power on delay is quite long */
- msleep(lis3->pwron_delay / lis3lv02d_get_odr());
-
/*
* Common configuration
* BDU: (12 bits sensors only) LSB and MSB values are not updated until
* both have been read. So the value read will always be correct.
+ * Set BOOT bit to refresh factory tuning values.
*/
- if (lis3->whoami == WAI_12B) {
- lis3->read(lis3, CTRL_REG2, &reg);
- reg |= CTRL2_BDU;
- lis3->write(lis3, CTRL_REG2, reg);
- }
+ lis3->read(lis3, CTRL_REG2, &reg);
+ if (lis3->whoami == WAI_12B)
+ reg |= CTRL2_BDU | CTRL2_BOOT;
+ else
+ reg |= CTRL2_BOOT_8B;
+ lis3->write(lis3, CTRL_REG2, reg);
+
+ /* LIS3 power on delay is quite long */
+ msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+
+ if (lis3->reg_ctrl)
+ lis3_context_restore(lis3);
}
EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
@@ -262,6 +403,27 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
mutex_unlock(&lis3_dev.mutex);
}
+static void lis3lv02d_joystick_open(struct input_polled_dev *pidev)
+{
+ if (lis3_dev.pm_dev)
+ pm_runtime_get_sync(lis3_dev.pm_dev);
+
+ if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev)
+ atomic_set(&lis3_dev.wake_thread, 1);
+ /*
+ * Update coordinates for the case where poll interval is 0 and
+ * the chip in running purely under interrupt control
+ */
+ lis3lv02d_joystick_poll(pidev);
+}
+
+static void lis3lv02d_joystick_close(struct input_polled_dev *pidev)
+{
+ atomic_set(&lis3_dev.wake_thread, 0);
+ if (lis3_dev.pm_dev)
+ pm_runtime_put(lis3_dev.pm_dev);
+}
+
static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
{
if (!test_bit(0, &lis3_dev.misc_opened))
@@ -277,8 +439,7 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
wake_up_interruptible(&lis3_dev.misc_wait);
kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);
out:
- if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev &&
- lis3_dev.idev->input->users)
+ if (atomic_read(&lis3_dev.wake_thread))
return IRQ_WAKE_THREAD;
return IRQ_HANDLED;
}
@@ -309,44 +470,41 @@ static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3)
mutex_unlock(&lis3->mutex);
}
-static void lis302dl_interrupt_handle_ff_wu(struct lis3lv02d *lis3)
+static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index)
{
- u8 wu1_src;
- u8 wu2_src;
-
- lis3->read(lis3, FF_WU_SRC_1, &wu1_src);
- lis3->read(lis3, FF_WU_SRC_2, &wu2_src);
+ int dummy;
- wu1_src = wu1_src & FF_WU_SRC_IA ? wu1_src : 0;
- wu2_src = wu2_src & FF_WU_SRC_IA ? wu2_src : 0;
-
- /* joystick poll is internally protected by the lis3->mutex. */
- if (wu1_src || wu2_src)
- lis3lv02d_joystick_poll(lis3_dev.idev);
+ /* Dummy read to ack interrupt */
+ lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy);
+ lis3->data_ready_count[index]++;
}
static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data)
{
-
struct lis3lv02d *lis3 = data;
+ u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK;
- if ((lis3->pdata->irq_cfg & LIS3_IRQ1_MASK) == LIS3_IRQ1_CLICK)
+ if (irq_cfg == LIS3_IRQ1_CLICK)
lis302dl_interrupt_handle_click(lis3);
+ else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY))
+ lis302dl_data_ready(lis3, IRQ_LINE0);
else
- lis302dl_interrupt_handle_ff_wu(lis3);
+ lis3lv02d_joystick_poll(lis3->idev);
return IRQ_HANDLED;
}
static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)
{
-
struct lis3lv02d *lis3 = data;
+ u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK;
- if ((lis3->pdata->irq_cfg & LIS3_IRQ2_MASK) == LIS3_IRQ2_CLICK)
+ if (irq_cfg == LIS3_IRQ2_CLICK)
lis302dl_interrupt_handle_click(lis3);
+ else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY))
+ lis302dl_data_ready(lis3, IRQ_LINE1);
else
- lis302dl_interrupt_handle_ff_wu(lis3);
+ lis3lv02d_joystick_poll(lis3->idev);
return IRQ_HANDLED;
}
@@ -356,6 +514,9 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
if (test_and_set_bit(0, &lis3_dev.misc_opened))
return -EBUSY; /* already open */
+ if (lis3_dev.pm_dev)
+ pm_runtime_get_sync(lis3_dev.pm_dev);
+
atomic_set(&lis3_dev.count, 0);
return 0;
}
@@ -364,6 +525,8 @@ static int lis3lv02d_misc_release(struct inode *inode, struct file *file)
{
fasync_helper(-1, file, 0, &lis3_dev.async_queue);
clear_bit(0, &lis3_dev.misc_opened); /* release the device */
+ if (lis3_dev.pm_dev)
+ pm_runtime_put(lis3_dev.pm_dev);
return 0;
}
@@ -460,6 +623,8 @@ int lis3lv02d_joystick_enable(void)
return -ENOMEM;
lis3_dev.idev->poll = lis3lv02d_joystick_poll;
+ lis3_dev.idev->open = lis3lv02d_joystick_open;
+ lis3_dev.idev->close = lis3lv02d_joystick_close;
lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN;
lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX;
@@ -473,8 +638,16 @@ int lis3lv02d_joystick_enable(void)
set_bit(EV_ABS, input_dev->evbit);
max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;
- fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY;
- flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY;
+ if (lis3_dev.whoami == WAI_12B) {
+ fuzz = LIS3_DEFAULT_FUZZ_12B;
+ flat = LIS3_DEFAULT_FLAT_12B;
+ } else {
+ fuzz = LIS3_DEFAULT_FUZZ_8B;
+ flat = LIS3_DEFAULT_FLAT_8B;
+ }
+ fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY;
+ flat = (flat * lis3_dev.scale) / LIS3_ACCURACY;
+
input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
@@ -512,14 +685,47 @@ void lis3lv02d_joystick_disable(void)
EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
/* Sysfs stuff */
+static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3)
+{
+ /*
+ * SYSFS functions are fast visitors so put-call
+ * immediately after the get-call. However, keep
+ * chip running for a while and schedule delayed
+ * suspend. This way periodic sysfs calls doesn't
+ * suffer from relatively long power up time.
+ */
+
+ if (lis3->pm_dev) {
+ pm_runtime_get_sync(lis3->pm_dev);
+ pm_runtime_put_noidle(lis3->pm_dev);
+ pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY);
+ }
+}
+
static ssize_t lis3lv02d_selftest_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int result;
s16 values[3];
- result = lis3lv02d_selftest(&lis3_dev, values);
- return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL",
+ static const char ok[] = "OK";
+ static const char fail[] = "FAIL";
+ static const char irq[] = "FAIL_IRQ";
+ const char *res;
+
+ lis3lv02d_sysfs_poweron(&lis3_dev);
+ switch (lis3lv02d_selftest(&lis3_dev, values)) {
+ case SELFTEST_FAIL:
+ res = fail;
+ break;
+ case SELFTEST_IRQ:
+ res = irq;
+ break;
+ case SELFTEST_OK:
+ default:
+ res = ok;
+ break;
+ }
+ return sprintf(buf, "%s %d %d %d\n", res,
values[0], values[1], values[2]);
}
@@ -528,6 +734,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev,
{
int x, y, z;
+ lis3lv02d_sysfs_poweron(&lis3_dev);
mutex_lock(&lis3_dev.mutex);
lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
mutex_unlock(&lis3_dev.mutex);
@@ -537,6 +744,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev,
static ssize_t lis3lv02d_rate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ lis3lv02d_sysfs_poweron(&lis3_dev);
return sprintf(buf, "%d\n", lis3lv02d_get_odr());
}
@@ -549,6 +757,7 @@ static ssize_t lis3lv02d_rate_set(struct device *dev,
if (strict_strtoul(buf, 0, &rate))
return -EINVAL;
+ lis3lv02d_sysfs_poweron(&lis3_dev);
if (lis3lv02d_set_odr(rate))
return -EINVAL;
@@ -585,6 +794,18 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
{
sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group);
platform_device_unregister(lis3->pdev);
+ if (lis3->pm_dev) {
+ /* Barrier after the sysfs remove */
+ pm_runtime_barrier(lis3->pm_dev);
+
+ /* SYSFS may have left chip running. Turn off if necessary */
+ if (!pm_runtime_suspended(lis3->pm_dev))
+ lis3lv02d_poweroff(&lis3_dev);
+
+ pm_runtime_disable(lis3->pm_dev);
+ pm_runtime_set_suspended(lis3->pm_dev);
+ }
+ kfree(lis3->reg_cache);
return 0;
}
EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
@@ -616,16 +837,16 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
if (p->wakeup_flags) {
dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
- /* default to 2.5ms for now */
- dev->write(dev, FF_WU_DURATION_1, 1);
+ /* pdata value + 1 to keep this backward compatible*/
+ dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1);
ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/
}
if (p->wakeup_flags2) {
dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2);
dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
- /* default to 2.5ms for now */
- dev->write(dev, FF_WU_DURATION_2, 1);
+ /* pdata value + 1 to keep this backward compatible*/
+ dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1);
ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/
}
/* Configure hipass filters */
@@ -635,8 +856,8 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
err = request_threaded_irq(p->irq2,
NULL,
lis302dl_interrupt_thread2_8b,
- IRQF_TRIGGER_RISING |
- IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT |
+ (p->irq_flags2 & IRQF_TRIGGER_MASK),
DRIVER_NAME, &lis3_dev);
if (err < 0)
printk(KERN_ERR DRIVER_NAME
@@ -652,6 +873,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
{
int err;
irq_handler_t thread_fn;
+ int irq_flags = 0;
dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
@@ -664,6 +886,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
dev->odrs = lis3_12_rates;
dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;
dev->scale = LIS3_SENSITIVITY_12B;
+ dev->regs = lis3_wai12_regs;
+ dev->regs_size = ARRAY_SIZE(lis3_wai12_regs);
break;
case WAI_8B:
printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n");
@@ -673,6 +897,17 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
dev->odrs = lis3_8_rates;
dev->odr_mask = CTRL1_DR;
dev->scale = LIS3_SENSITIVITY_8B;
+ dev->regs = lis3_wai8_regs;
+ dev->regs_size = ARRAY_SIZE(lis3_wai8_regs);
+ break;
+ case WAI_3DC:
+ printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n");
+ dev->read_data = lis3lv02d_read_8;
+ dev->mdps_max_val = 128;
+ dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ dev->odrs = lis3_3dc_rates;
+ dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
+ dev->scale = LIS3_SENSITIVITY_8B;
break;
default:
printk(KERN_ERR DRIVER_NAME
@@ -680,11 +915,25 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
return -EINVAL;
}
+ dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs),
+ sizeof(lis3_wai12_regs)), GFP_KERNEL);
+
+ if (dev->reg_cache == NULL) {
+ printk(KERN_ERR DRIVER_NAME "out of memory\n");
+ return -ENOMEM;
+ }
+
mutex_init(&dev->mutex);
+ atomic_set(&dev->wake_thread, 0);
lis3lv02d_add_fs(dev);
lis3lv02d_poweron(dev);
+ if (dev->pm_dev) {
+ pm_runtime_set_active(dev->pm_dev);
+ pm_runtime_enable(dev->pm_dev);
+ }
+
if (lis3lv02d_joystick_enable())
printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n");
@@ -696,8 +945,14 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
if (dev->whoami == WAI_8B)
lis3lv02d_8b_configure(dev, p);
+ irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK;
+
+ dev->irq_cfg = p->irq_cfg;
if (p->irq_cfg)
dev->write(dev, CTRL_REG3, p->irq_cfg);
+
+ if (p->default_rate)
+ lis3lv02d_set_odr(p->default_rate);
}
/* bail if we did not get an IRQ from the bus layer */
@@ -725,7 +980,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
err = request_threaded_irq(dev->irq, lis302dl_interrupt,
thread_fn,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT |
+ irq_flags,
DRIVER_NAME, &lis3_dev);
if (err < 0) {
diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h
index 854091380e33..a1939589eb2c 100644
--- a/drivers/hwmon/lis3lv02d.h
+++ b/drivers/hwmon/lis3lv02d.h
@@ -20,6 +20,7 @@
*/
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
+#include <linux/regulator/consumer.h>
/*
* This driver tries to support the "digital" accelerometer chips from
@@ -45,6 +46,7 @@ enum lis3_reg {
CTRL_REG1 = 0x20,
CTRL_REG2 = 0x21,
CTRL_REG3 = 0x22,
+ CTRL_REG4 = 0x23,
HP_FILTER_RESET = 0x23,
STATUS_REG = 0x27,
OUTX_L = 0x28,
@@ -93,6 +95,7 @@ enum lis3lv02d_reg {
};
enum lis3_who_am_i {
+ WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */
WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
@@ -118,6 +121,13 @@ enum lis3lv02d_ctrl1_8b {
CTRL1_DR = 0x80,
};
+enum lis3lv02d_ctrl1_3dc {
+ CTRL1_ODR0 = 0x10,
+ CTRL1_ODR1 = 0x20,
+ CTRL1_ODR2 = 0x40,
+ CTRL1_ODR3 = 0x80,
+};
+
enum lis3lv02d_ctrl2 {
CTRL2_DAS = 0x01,
CTRL2_SIM = 0x02,
@@ -129,9 +139,18 @@ enum lis3lv02d_ctrl2 {
CTRL2_FS = 0x80, /* Full Scale selection */
};
+enum lis3lv02d_ctrl4_3dc {
+ CTRL4_SIM = 0x01,
+ CTRL4_ST0 = 0x02,
+ CTRL4_ST1 = 0x04,
+ CTRL4_FS0 = 0x10,
+ CTRL4_FS1 = 0x20,
+};
+
enum lis302d_ctrl2 {
HP_FF_WU2 = 0x08,
HP_FF_WU1 = 0x04,
+ CTRL2_BOOT_8B = 0x40,
};
enum lis3lv02d_ctrl3 {
@@ -206,19 +225,33 @@ enum lis3lv02d_click_src_8b {
CLICK_IA = 0x40,
};
-struct axis_conversion {
- s8 x;
- s8 y;
- s8 z;
+enum lis3lv02d_reg_state {
+ LIS3_REG_OFF = 0x00,
+ LIS3_REG_ON = 0x01,
+};
+
+union axis_conversion {
+ struct {
+ int x, y, z;
+ };
+ int as_array[3];
+
};
struct lis3lv02d {
void *bus_priv; /* used by the bus layer only */
+ struct device *pm_dev; /* for pm_runtime purposes */
int (*init) (struct lis3lv02d *lis3);
int (*write) (struct lis3lv02d *lis3, int reg, u8 val);
int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret);
+ int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret);
+ int (*reg_ctrl) (struct lis3lv02d *lis3, bool state);
int *odrs; /* Supported output data rates */
+ u8 *regs; /* Regs to store / restore */
+ int regs_size;
+ u8 *reg_cache;
+ bool regs_stored;
u8 odr_mask; /* ODR bit mask */
u8 whoami; /* indicates measurement precision */
s16 (*read_data) (struct lis3lv02d *lis3, int reg);
@@ -231,14 +264,18 @@ struct lis3lv02d {
struct input_polled_dev *idev; /* input device */
struct platform_device *pdev; /* platform device */
+ struct regulator_bulk_data regulators[2];
atomic_t count; /* interrupt count after last read */
- struct axis_conversion ac; /* hw -> logical axis */
+ union axis_conversion ac; /* hw -> logical axis */
int mapped_btns[3];
u32 irq; /* IRQ number */
struct fasync_struct *async_queue; /* queue for the misc device */
wait_queue_head_t misc_wait; /* Wait queue for the misc device */
unsigned long misc_opened; /* bit0: whether the device is open */
+ int data_ready_count[2];
+ atomic_t wake_thread;
+ unsigned char irq_cfg;
struct lis3lv02d_platform_data *pdata; /* for passing board config */
struct mutex mutex; /* Serialize poll and selftest */
diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c
index 8e5933b72d19..9f4bae07f719 100644
--- a/drivers/hwmon/lis3lv02d_i2c.c
+++ b/drivers/hwmon/lis3lv02d_i2c.c
@@ -29,10 +29,30 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
#include "lis3lv02d.h"
#define DRV_NAME "lis3lv02d_i2c"
+static const char reg_vdd[] = "Vdd";
+static const char reg_vdd_io[] = "Vdd_IO";
+
+static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state)
+{
+ int ret;
+ if (state == LIS3_REG_OFF) {
+ ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators),
+ lis3->regulators);
+ } else {
+ ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators),
+ lis3->regulators);
+ /* Chip needs time to wakeup. Not mentioned in datasheet */
+ usleep_range(10000, 20000);
+ }
+ return ret;
+}
+
static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value)
{
struct i2c_client *c = lis3->bus_priv;
@@ -46,24 +66,38 @@ static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v)
return 0;
}
+static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len,
+ u8 *v)
+{
+ struct i2c_client *c = lis3->bus_priv;
+ reg |= (1 << 7); /* 7th bit enables address auto incrementation */
+ return i2c_smbus_read_i2c_block_data(c, reg, len, v);
+}
+
static int lis3_i2c_init(struct lis3lv02d *lis3)
{
u8 reg;
int ret;
+ if (lis3->reg_ctrl)
+ lis3_reg_ctrl(lis3, LIS3_REG_ON);
+
+ lis3->read(lis3, WHO_AM_I, &reg);
+ if (reg != lis3->whoami)
+ printk(KERN_ERR "lis3: power on failure\n");
+
/* power up the device */
ret = lis3->read(lis3, CTRL_REG1, &reg);
if (ret < 0)
return ret;
- reg |= CTRL1_PD0;
+ reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
return lis3->write(lis3, CTRL_REG1, reg);
}
/* Default axis mapping but it can be overwritten by platform data */
-static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X,
- LIS3_DEV_Y,
- LIS3_DEV_Z };
+static union axis_conversion lis3lv02d_axis_map =
+ { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@@ -72,6 +106,15 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
if (pdata) {
+ /* Regulator control is optional */
+ if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL)
+ lis3_dev.reg_ctrl = lis3_reg_ctrl;
+
+ if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
+ (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_I2C_BLOCK)))
+ lis3_dev.blkread = lis3_i2c_blockread;
+
if (pdata->axis_x)
lis3lv02d_axis_map.x = pdata->axis_x;
@@ -88,6 +131,16 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
goto fail;
}
+ if (lis3_dev.reg_ctrl) {
+ lis3_dev.regulators[0].supply = reg_vdd;
+ lis3_dev.regulators[1].supply = reg_vdd_io;
+ ret = regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(lis3_dev.regulators),
+ lis3_dev.regulators);
+ if (ret < 0)
+ goto fail;
+ }
+
lis3_dev.pdata = pdata;
lis3_dev.bus_priv = client;
lis3_dev.init = lis3_i2c_init;
@@ -95,10 +148,24 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
lis3_dev.write = lis3_i2c_write;
lis3_dev.irq = client->irq;
lis3_dev.ac = lis3lv02d_axis_map;
+ lis3_dev.pm_dev = &client->dev;
i2c_set_clientdata(client, &lis3_dev);
+
+ /* Provide power over the init call */
+ if (lis3_dev.reg_ctrl)
+ lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON);
+
ret = lis3lv02d_init_device(&lis3_dev);
+
+ if (lis3_dev.reg_ctrl)
+ lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF);
+
+ if (ret == 0)
+ return 0;
fail:
+ if (pdata && pdata->release_resources)
+ pdata->release_resources();
return ret;
}
@@ -111,14 +178,18 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client)
pdata->release_resources();
lis3lv02d_joystick_disable();
- lis3lv02d_poweroff(lis3);
+ lis3lv02d_remove_fs(&lis3_dev);
- return lis3lv02d_remove_fs(&lis3_dev);
+ if (lis3_dev.reg_ctrl)
+ regulator_bulk_free(ARRAY_SIZE(lis3->regulators),
+ lis3_dev.regulators);
+ return 0;
}
#ifdef CONFIG_PM
-static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+static int lis3lv02d_i2c_suspend(struct device *dev)
{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
@@ -126,18 +197,21 @@ static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int lis3lv02d_i2c_resume(struct i2c_client *client)
+static int lis3lv02d_i2c_resume(struct device *dev)
{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct lis3lv02d *lis3 = i2c_get_clientdata(client);
- if (!lis3->pdata || !lis3->pdata->wakeup_flags)
+ /*
+ * pm_runtime documentation says that devices should always
+ * be powered on at resume. Pm_runtime turns them off after system
+ * wide resume is complete.
+ */
+ if (!lis3->pdata || !lis3->pdata->wakeup_flags ||
+ pm_runtime_suspended(dev))
lis3lv02d_poweron(lis3);
- return 0;
-}
-static void lis3lv02d_i2c_shutdown(struct i2c_client *client)
-{
- lis3lv02d_i2c_suspend(client, PMSG_SUSPEND);
+ return 0;
}
#else
#define lis3lv02d_i2c_suspend NULL
@@ -145,6 +219,24 @@ static void lis3lv02d_i2c_shutdown(struct i2c_client *client)
#define lis3lv02d_i2c_shutdown NULL
#endif
+static int lis3_i2c_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+
+ lis3lv02d_poweroff(lis3);
+ return 0;
+}
+
+static int lis3_i2c_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+
+ lis3lv02d_poweron(lis3);
+ return 0;
+}
+
static const struct i2c_device_id lis3lv02d_id[] = {
{"lis3lv02d", 0 },
{}
@@ -152,14 +244,20 @@ static const struct i2c_device_id lis3lv02d_id[] = {
MODULE_DEVICE_TABLE(i2c, lis3lv02d_id);
+static const struct dev_pm_ops lis3_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend,
+ lis3lv02d_i2c_resume)
+ SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend,
+ lis3_i2c_runtime_resume,
+ NULL)
+};
+
static struct i2c_driver lis3lv02d_i2c_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .pm = &lis3_pm_ops,
},
- .suspend = lis3lv02d_i2c_suspend,
- .shutdown = lis3lv02d_i2c_shutdown,
- .resume = lis3lv02d_i2c_resume,
.probe = lis3lv02d_i2c_probe,
.remove = __devexit_p(lis3lv02d_i2c_remove),
.id_table = lis3lv02d_id,
diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c
index b9be5e3a22b3..2549de1de4e2 100644
--- a/drivers/hwmon/lis3lv02d_spi.c
+++ b/drivers/hwmon/lis3lv02d_spi.c
@@ -50,11 +50,12 @@ static int lis3_spi_init(struct lis3lv02d *lis3)
if (ret < 0)
return ret;
- reg |= CTRL1_PD0;
+ reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
return lis3->write(lis3, CTRL_REG1, reg);
}
-static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 };
+static union axis_conversion lis3lv02d_axis_normal =
+ { .as_array = { 1, 2, 3 } };
static int __devinit lis302dl_spi_probe(struct spi_device *spi)
{
diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c
new file mode 100644
index 000000000000..267626178678
--- /dev/null
+++ b/drivers/hwmon/ltc4261.c
@@ -0,0 +1,315 @@
+/*
+ * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller
+ *
+ * Copyright (C) 2010 Ericsson AB.
+ *
+ * Derived from:
+ *
+ * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller
+ * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+/* chip registers */
+#define LTC4261_STATUS 0x00 /* readonly */
+#define LTC4261_FAULT 0x01
+#define LTC4261_ALERT 0x02
+#define LTC4261_CONTROL 0x03
+#define LTC4261_SENSE_H 0x04
+#define LTC4261_SENSE_L 0x05
+#define LTC4261_ADIN2_H 0x06
+#define LTC4261_ADIN2_L 0x07
+#define LTC4261_ADIN_H 0x08
+#define LTC4261_ADIN_L 0x09
+
+/*
+ * Fault register bits
+ */
+#define FAULT_OV (1<<0)
+#define FAULT_UV (1<<1)
+#define FAULT_OC (1<<2)
+
+struct ltc4261_data {
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ /* Registers */
+ u8 regs[10];
+};
+
+static struct ltc4261_data *ltc4261_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc4261_data *data = i2c_get_clientdata(client);
+ struct ltc4261_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) {
+ int i;
+
+ /* Read registers -- 0x00 to 0x09 */
+ for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, i);
+ if (unlikely(val < 0)) {
+ dev_dbg(dev,
+ "Failed to read ADC value: error %d",
+ val);
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->regs[i] = val;
+ }
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+/* Return the voltage from the given register in mV or mA */
+static int ltc4261_get_value(struct ltc4261_data *data, u8 reg)
+{
+ u32 val;
+
+ val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6);
+
+ switch (reg) {
+ case LTC4261_ADIN_H:
+ case LTC4261_ADIN2_H:
+ /* 2.5mV resolution. Convert to mV. */
+ val = val * 25 / 10;
+ break;
+ case LTC4261_SENSE_H:
+ /*
+ * 62.5uV resolution. Convert to current as measured with
+ * an 1 mOhm sense resistor, in mA. If a different sense
+ * resistor is installed, calculate the actual current by
+ * dividing the reported current by the sense resistor value
+ * in mOhm.
+ */
+ val = val * 625 / 10;
+ break;
+ default:
+ /* If we get here, the developer messed up */
+ WARN_ON_ONCE(1);
+ val = 0;
+ break;
+ }
+
+ return val;
+}
+
+static ssize_t ltc4261_show_value(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc4261_data *data = ltc4261_update_device(dev);
+ int value;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ value = ltc4261_get_value(data, attr->index);
+ return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+static ssize_t ltc4261_show_bool(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc4261_data *data = ltc4261_update_device(dev);
+ u8 fault;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ fault = data->regs[LTC4261_FAULT] & attr->index;
+ if (fault) /* Clear reported faults in chip register */
+ i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0);
+}
+
+/*
+ * These macros are used below in constructing device attribute objects
+ * for use with sysfs_create_group() to make a sysfs device file
+ * for each register.
+ */
+
+#define LTC4261_VALUE(name, ltc4261_cmd_idx) \
+ static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
+ ltc4261_show_value, NULL, ltc4261_cmd_idx)
+
+#define LTC4261_BOOL(name, mask) \
+ static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
+ ltc4261_show_bool, NULL, (mask))
+
+/*
+ * Input voltages.
+ */
+LTC4261_VALUE(in1_input, LTC4261_ADIN_H);
+LTC4261_VALUE(in2_input, LTC4261_ADIN2_H);
+
+/*
+ * Voltage alarms. The chip has only one set of voltage alarm status bits,
+ * triggered by input voltage alarms. In many designs, those alarms are
+ * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin
+ * to the OV pin. ADIN2 is, however, not available on all chip variants.
+ * To ensure that the alarm condition is reported to the user, report it
+ * with both voltage sensors.
+ */
+LTC4261_BOOL(in1_min_alarm, FAULT_UV);
+LTC4261_BOOL(in1_max_alarm, FAULT_OV);
+LTC4261_BOOL(in2_min_alarm, FAULT_UV);
+LTC4261_BOOL(in2_max_alarm, FAULT_OV);
+
+/* Currents (via sense resistor) */
+LTC4261_VALUE(curr1_input, LTC4261_SENSE_H);
+
+/* Overcurrent alarm */
+LTC4261_BOOL(curr1_max_alarm, FAULT_OC);
+
+static struct attribute *ltc4261_attributes[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
+
+ NULL,
+};
+
+static const struct attribute_group ltc4261_group = {
+ .attrs = ltc4261_attributes,
+};
+
+static int ltc4261_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct ltc4261_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) {
+ dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n",
+ adapter->id, client->addr, LTC4261_STATUS);
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto out_kzalloc;
+ }
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* Clear faults */
+ i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00);
+
+ /* Register sysfs hooks */
+ ret = sysfs_create_group(&client->dev.kobj, &ltc4261_group);
+ if (ret)
+ goto out_sysfs_create_group;
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ goto out_hwmon_device_register;
+ }
+
+ return 0;
+
+out_hwmon_device_register:
+ sysfs_remove_group(&client->dev.kobj, &ltc4261_group);
+out_sysfs_create_group:
+ kfree(data);
+out_kzalloc:
+ return ret;
+}
+
+static int ltc4261_remove(struct i2c_client *client)
+{
+ struct ltc4261_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &ltc4261_group);
+
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id ltc4261_id[] = {
+ {"ltc4261", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ltc4261_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ltc4261_driver = {
+ .driver = {
+ .name = "ltc4261",
+ },
+ .probe = ltc4261_probe,
+ .remove = ltc4261_remove,
+ .id_table = ltc4261_id,
+};
+
+static int __init ltc4261_init(void)
+{
+ return i2c_add_driver(&ltc4261_driver);
+}
+
+static void __exit ltc4261_exit(void)
+{
+ i2c_del_driver(&ltc4261_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
+MODULE_DESCRIPTION("LTC4261 driver");
+MODULE_LICENSE("GPL");
+
+module_init(ltc4261_init);
+module_exit(ltc4261_exit);
diff --git a/drivers/hwmon/pkgtemp.c b/drivers/hwmon/pkgtemp.c
index f11903936c8b..0798210590bc 100644
--- a/drivers/hwmon/pkgtemp.c
+++ b/drivers/hwmon/pkgtemp.c
@@ -21,7 +21,6 @@
*/
#include <linux/module.h>
-#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
@@ -35,6 +34,7 @@
#include <linux/cpu.h>
#include <asm/msr.h>
#include <asm/processor.h>
+#include <asm/smp.h>
#define DRVNAME "pkgtemp"
@@ -339,8 +339,7 @@ exit:
return err;
}
-#ifdef CONFIG_HOTPLUG_CPU
-static void pkgtemp_device_remove(unsigned int cpu)
+static void __cpuinit pkgtemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p;
unsigned int i;
@@ -387,12 +386,10 @@ static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb,
static struct notifier_block pkgtemp_cpu_notifier __refdata = {
.notifier_call = pkgtemp_cpu_callback,
};
-#endif /* !CONFIG_HOTPLUG_CPU */
static int __init pkgtemp_init(void)
{
int i, err = -ENODEV;
- struct pdev_entry *p, *n;
/* quick check if we run Intel */
if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
@@ -402,31 +399,23 @@ static int __init pkgtemp_init(void)
if (err)
goto exit;
- for_each_online_cpu(i) {
- err = pkgtemp_device_add(i);
- if (err)
- goto exit_devices_unreg;
- }
+ for_each_online_cpu(i)
+ pkgtemp_device_add(i);
+
+#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
err = -ENODEV;
goto exit_driver_unreg;
}
+#endif
-#ifdef CONFIG_HOTPLUG_CPU
register_hotcpu_notifier(&pkgtemp_cpu_notifier);
-#endif
return 0;
-exit_devices_unreg:
- mutex_lock(&pdev_list_mutex);
- list_for_each_entry_safe(p, n, &pdev_list, list) {
- platform_device_unregister(p->pdev);
- list_del(&p->list);
- kfree(p);
- }
- mutex_unlock(&pdev_list_mutex);
+#ifndef CONFIG_HOTPLUG_CPU
exit_driver_unreg:
platform_driver_unregister(&pkgtemp_driver);
+#endif
exit:
return err;
}
@@ -434,9 +423,8 @@ exit:
static void __exit pkgtemp_exit(void)
{
struct pdev_entry *p, *n;
-#ifdef CONFIG_HOTPLUG_CPU
+
unregister_hotcpu_notifier(&pkgtemp_cpu_notifier);
-#endif
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index ffb793af680b..ec7fad747adc 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -22,10 +22,8 @@
*/
#include <linux/module.h>
-#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/sysfs.h>
#include <linux/hwmon-sysfs.h>
@@ -237,8 +235,7 @@ exit:
return err;
}
-#ifdef CONFIG_HOTPLUG_CPU
-static void via_cputemp_device_remove(unsigned int cpu)
+static void __cpuinit via_cputemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p, *n;
mutex_lock(&pdev_list_mutex);
@@ -272,7 +269,6 @@ static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb,
static struct notifier_block via_cputemp_cpu_notifier __refdata = {
.notifier_call = via_cputemp_cpu_callback,
};
-#endif /* !CONFIG_HOTPLUG_CPU */
static int __init via_cputemp_init(void)
{
@@ -313,9 +309,7 @@ static int __init via_cputemp_init(void)
goto exit_driver_unreg;
}
-#ifdef CONFIG_HOTPLUG_CPU
register_hotcpu_notifier(&via_cputemp_cpu_notifier);
-#endif
return 0;
exit_devices_unreg:
@@ -335,9 +329,8 @@ exit:
static void __exit via_cputemp_exit(void)
{
struct pdev_entry *p, *n;
-#ifdef CONFIG_HOTPLUG_CPU
+
unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
-#endif
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c
index 4f93da31d3ad..3cad8fecc3d3 100644
--- a/drivers/i2c/busses/i2c-sh7760.c
+++ b/drivers/i2c/busses/i2c-sh7760.c
@@ -101,12 +101,12 @@ struct cami2c {
static inline void OUT32(struct cami2c *cam, int reg, unsigned long val)
{
- ctrl_outl(val, (unsigned long)cam->iobase + reg);
+ __raw_writel(val, (unsigned long)cam->iobase + reg);
}
static inline unsigned long IN32(struct cami2c *cam, int reg)
{
- return ctrl_inl((unsigned long)cam->iobase + reg);
+ return __raw_readl((unsigned long)cam->iobase + reg);
}
static irqreturn_t sh7760_i2c_irq(int irq, void *ptr)
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 598c49acaeb5..2707f5e17158 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -538,15 +538,17 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
{
struct resource *res;
int ret = -ENXIO;
- int q, m;
- int k = 0;
- int n = 0;
+ int n, k = 0;
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
for (n = res->start; hook && n <= res->end; n++) {
if (request_irq(n, sh_mobile_i2c_isr, IRQF_DISABLED,
- dev_name(&dev->dev), dev))
+ dev_name(&dev->dev), dev)) {
+ for (n--; n >= res->start; n--)
+ free_irq(n, dev);
+
goto rollback;
+ }
}
k++;
}
@@ -554,16 +556,17 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
if (hook)
return k > 0 ? 0 : -ENOENT;
- k--;
ret = 0;
rollback:
- for (q = k; k >= 0; k--) {
- for (m = n; m >= res->start; m--)
- free_irq(m, dev);
+ k--;
+
+ while (k >= 0) {
+ res = platform_get_resource(dev, IORESOURCE_IRQ, k);
+ for (n = res->start; n <= res->end; n++)
+ free_irq(n, dev);
- res = platform_get_resource(dev, IORESOURCE_IRQ, k - 1);
- m = res->end;
+ k--;
}
return ret;
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index cb3ccf3ed221..41665d2f9f93 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -74,7 +74,7 @@ static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1;
static unsigned int mwait_substates;
/* Reliable LAPIC Timer States, bit 1 for C1 etc. */
-static unsigned int lapic_timer_reliable_states;
+static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */
static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
@@ -94,7 +94,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
.driver_data = (void *) 0x00,
.flags = CPUIDLE_FLAG_TIME_VALID,
.exit_latency = 3,
- .power_usage = 1000,
.target_residency = 6,
.enter = &intel_idle },
{ /* MWAIT C2 */
@@ -103,7 +102,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
.driver_data = (void *) 0x10,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 20,
- .power_usage = 500,
.target_residency = 80,
.enter = &intel_idle },
{ /* MWAIT C3 */
@@ -112,11 +110,46 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
.driver_data = (void *) 0x20,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 200,
- .power_usage = 350,
.target_residency = 800,
.enter = &intel_idle },
};
+static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
+ { /* MWAIT C0 */ },
+ { /* MWAIT C1 */
+ .name = "SNB-C1",
+ .desc = "MWAIT 0x00",
+ .driver_data = (void *) 0x00,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 1,
+ .target_residency = 4,
+ .enter = &intel_idle },
+ { /* MWAIT C2 */
+ .name = "SNB-C3",
+ .desc = "MWAIT 0x10",
+ .driver_data = (void *) 0x10,
+ .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 80,
+ .target_residency = 160,
+ .enter = &intel_idle },
+ { /* MWAIT C3 */
+ .name = "SNB-C6",
+ .desc = "MWAIT 0x20",
+ .driver_data = (void *) 0x20,
+ .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 104,
+ .target_residency = 208,
+ .enter = &intel_idle },
+ { /* MWAIT C4 */
+ .name = "SNB-C7",
+ .desc = "MWAIT 0x30",
+ .driver_data = (void *) 0x30,
+ .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 109,
+ .target_residency = 300,
+ .enter = &intel_idle },
+};
+
static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
{ /* MWAIT C0 */ },
{ /* MWAIT C1 */
@@ -125,7 +158,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
.driver_data = (void *) 0x00,
.flags = CPUIDLE_FLAG_TIME_VALID,
.exit_latency = 1,
- .power_usage = 1000,
.target_residency = 4,
.enter = &intel_idle },
{ /* MWAIT C2 */
@@ -134,7 +166,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
.driver_data = (void *) 0x10,
.flags = CPUIDLE_FLAG_TIME_VALID,
.exit_latency = 20,
- .power_usage = 500,
.target_residency = 80,
.enter = &intel_idle },
{ /* MWAIT C3 */ },
@@ -144,7 +175,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
.driver_data = (void *) 0x30,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 100,
- .power_usage = 250,
.target_residency = 400,
.enter = &intel_idle },
{ /* MWAIT C5 */ },
@@ -154,7 +184,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
.driver_data = (void *) 0x52,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 140,
- .power_usage = 150,
.target_residency = 560,
.enter = &intel_idle },
};
@@ -179,13 +208,10 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
local_irq_disable();
/*
- * If the state flag indicates that the TLB will be flushed or if this
- * is the deepest c-state supported, do a voluntary leave mm to avoid
- * costly and mostly unnecessary wakeups for flushing the user TLB's
- * associated with the active mm.
+ * leave_mm() to avoid costly and often unnecessary wakeups
+ * for flushing the user TLB's associated with the active mm.
*/
- if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED ||
- (&dev->states[dev->state_count - 1] == state))
+ if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED)
leave_mm(cpu);
if (!(lapic_timer_reliable_states & (1 << (cstate))))
@@ -269,9 +295,14 @@ static int intel_idle_probe(void)
case 0x1C: /* 28 - Atom Processor */
case 0x26: /* 38 - Lincroft Atom Processor */
- lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */
+ lapic_timer_reliable_states = (1 << 1); /* C1 */
cpuidle_state_table = atom_cstates;
break;
+
+ case 0x2A: /* SNB */
+ case 0x2D: /* SNB Xeon */
+ cpuidle_state_table = snb_cstates;
+ break;
#ifdef FUTURE_USE
case 0x17: /* 23 - Core 2 Duo */
lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */
diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig
deleted file mode 100644
index e02096cf7d95..000000000000
--- a/drivers/ieee1394/Kconfig
+++ /dev/null
@@ -1,182 +0,0 @@
-config IEEE1394
- tristate "Legacy alternative FireWire driver stack"
- depends on PCI || BROKEN
- help
- IEEE 1394 describes a high performance serial bus, which is also
- known as FireWire(tm) or i.Link(tm) and is used for connecting all
- sorts of devices (most notably digital video cameras) to your
- computer.
-
- If you have FireWire hardware and want to use it, say Y here. This
- is the core support only, you will also need to select a driver for
- your IEEE 1394 adapter.
-
- To compile this driver as a module, say M here: the module will be
- called ieee1394.
-
- NOTE:
- ieee1394 is superseded by the newer firewire-core driver. See
- http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
- further information on how to switch to the new FireWire drivers.
-
-config IEEE1394_OHCI1394
- tristate "OHCI-1394 controllers"
- depends on PCI && IEEE1394
- help
- Enable this driver if you have an IEEE 1394 controller based on the
- OHCI-1394 specification. The current driver is only tested with OHCI
- chipsets made by Texas Instruments and NEC. Most third-party vendors
- use one of these chipsets. It should work with any OHCI-1394
- compliant card, however.
-
- To compile this driver as a module, say M here: the module will be
- called ohci1394.
-
- NOTE:
- ohci1394 is superseded by the newer firewire-ohci driver. See
- http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
- further information on how to switch to the new FireWire drivers.
-
- If you want to install firewire-ohci and ohci1394 together, you
- should configure them only as modules and blacklist the driver(s)
- which you don't want to have auto-loaded. Add either
-
- blacklist ohci1394
- blacklist video1394
- blacklist dv1394
- or
- blacklist firewire-ohci
-
- to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf
- depending on your distribution.
-
-comment "PCILynx controller requires I2C"
- depends on IEEE1394 && I2C=n
-
-config IEEE1394_PCILYNX
- tristate "PCILynx controller"
- depends on PCI && IEEE1394 && I2C
- select I2C_ALGOBIT
- help
- Say Y here if you have an IEEE-1394 controller with the Texas
- Instruments PCILynx chip. Note: this driver is written for revision
- 2 of this chip and may not work with revision 0.
-
- To compile this driver as a module, say M here: the module will be
- called pcilynx.
-
- Only some old and now very rare PCI and CardBus cards and
- PowerMacs G3 B&W contain the PCILynx controller. Therefore
- almost everybody can say N here.
-
-comment "SBP-2 support (for storage devices) requires SCSI"
- depends on IEEE1394 && SCSI=n
-
-config IEEE1394_SBP2
- tristate "Storage devices (SBP-2 protocol)"
- depends on IEEE1394 && SCSI
- help
- This option enables you to use SBP-2 devices connected to an IEEE
- 1394 bus. SBP-2 devices include storage devices like harddisks and
- DVD drives, also some other FireWire devices like scanners.
-
- You should also enable support for disks, CD-ROMs, etc. in the SCSI
- configuration section.
-
- To compile this driver as a module, say M here: the module will be
- called sbp2.
-
- NOTE:
- sbp2 is superseded by the newer firewire-sbp2 driver. See
- http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
- further information on how to switch to the new FireWire drivers.
-
-config IEEE1394_SBP2_PHYS_DMA
- bool "Enable replacement for physical DMA in SBP2"
- depends on IEEE1394_SBP2 && VIRT_TO_BUS && EXPERIMENTAL
- help
- This builds sbp2 for use with non-OHCI host adapters which do not
- support physical DMA or for when ohci1394 is run with phys_dma=0.
- Physical DMA is data movement without assistance of the drivers'
- interrupt handlers. This option includes the interrupt handlers
- that are required in absence of this hardware feature.
-
- This option is buggy and currently broken on some architectures.
- If unsure, say N.
-
-config IEEE1394_ETH1394_ROM_ENTRY
- depends on IEEE1394
- bool
- default n
-
-config IEEE1394_ETH1394
- tristate "IP networking over 1394 (experimental)"
- depends on IEEE1394 && EXPERIMENTAL && INET
- select IEEE1394_ETH1394_ROM_ENTRY
- help
- This driver implements a functional majority of RFC 2734: IPv4 over
- 1394. It will provide IP connectivity with implementations of RFC
- 2734 found on other operating systems. It will not communicate with
- older versions of this driver found in stock kernels prior to 2.6.3.
- This driver is still considered experimental. It does not yet support
- MCAP, therefore multicast support is significantly limited.
-
- The module is called eth1394 although it does not emulate Ethernet.
-
- NOTE:
- eth1394 is superseded by the newer firewire-net driver. See
- http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
- further information on how to switch to the new FireWire drivers.
-
-config IEEE1394_RAWIO
- tristate "raw1394 userspace interface"
- depends on IEEE1394
- help
- This option adds support for the raw1394 device file which enables
- direct communication of user programs with IEEE 1394 devices
- (isochronous and asynchronous). Almost all application programs
- which access FireWire require this option.
-
- To compile this driver as a module, say M here: the module will be
- called raw1394.
-
- NOTE:
- raw1394 is superseded by the newer firewire-core driver. See
- http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
- further information on how to switch to the new FireWire drivers.
-
-config IEEE1394_VIDEO1394
- tristate "video1394 userspace interface"
- depends on IEEE1394 && IEEE1394_OHCI1394
- help
- This option adds support for the video1394 device files which enable
- isochronous communication of user programs with IEEE 1394 devices,
- especially video capture or export. This interface is used by all
- libdc1394 based programs and by several other programs, in addition to
- the raw1394 interface. It is generally not required for DV capture.
-
- To compile this driver as a module, say M here: the module will be
- called video1394.
-
- NOTE:
- video1394 is superseded by the newer firewire-core driver. See
- http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
- further information on how to switch to the new FireWire drivers.
-
-config IEEE1394_DV1394
- tristate "dv1394 userspace interface (deprecated)"
- depends on IEEE1394 && IEEE1394_OHCI1394
- help
- The dv1394 driver is unsupported and may be removed from Linux in a
- future release. Its functionality is now provided by either
- raw1394 or firewire-core together with libraries such as libiec61883.
-
-config IEEE1394_VERBOSEDEBUG
- bool "Excessive debugging output"
- depends on IEEE1394
- help
- If you say Y here, you will get very verbose debugging logs from the
- ieee1394 drivers, including sent and received packet headers. This
- will quickly result in large amounts of data sent to the system log.
-
- Say Y if you really need the debugging output. Everyone else says N.
diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile
deleted file mode 100644
index 1f8153b57503..000000000000
--- a/drivers/ieee1394/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Makefile for the Linux IEEE 1394 implementation
-#
-
-ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \
- highlevel.o csr.o nodemgr.o dma.o iso.o \
- csr1212.o config_roms.o
-
-obj-$(CONFIG_IEEE1394) += ieee1394.o
-obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o
-obj-$(CONFIG_IEEE1394_OHCI1394) += ohci1394.o
-obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o
-obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o
-obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o
-obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o
-obj-$(CONFIG_IEEE1394_ETH1394) += eth1394.o
-
-obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o
diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c
deleted file mode 100644
index 1b981207fa76..000000000000
--- a/drivers/ieee1394/config_roms.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * ConfigROM entries
- *
- * Copyright (C) 2004 Ben Collins
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/types.h>
-
-#include "csr1212.h"
-#include "ieee1394.h"
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "csr.h"
-#include "config_roms.h"
-
-struct hpsb_config_rom_entry {
- const char *name;
-
- /* Base initialization, called at module load */
- int (*init)(void);
-
- /* Cleanup called at module exit */
- void (*cleanup)(void);
-
- /* The flag added to host->config_roms */
- unsigned int flag;
-};
-
-/* The default host entry. This must succeed. */
-int hpsb_default_host_entry(struct hpsb_host *host)
-{
- struct csr1212_keyval *root;
- struct csr1212_keyval *vend_id = NULL;
- struct csr1212_keyval *text = NULL;
- char csr_name[128];
- int ret;
-
- sprintf(csr_name, "Linux - %s", host->driver->name);
- root = host->csr.rom->root_kv;
-
- vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR, host->csr.guid_hi >> 8);
- text = csr1212_new_string_descriptor_leaf(csr_name);
-
- if (!vend_id || !text) {
- if (vend_id)
- csr1212_release_keyval(vend_id);
- if (text)
- csr1212_release_keyval(text);
- csr1212_destroy_csr(host->csr.rom);
- return -ENOMEM;
- }
-
- csr1212_associate_keyval(vend_id, text);
- csr1212_release_keyval(text);
- ret = csr1212_attach_keyval_to_directory(root, vend_id);
- csr1212_release_keyval(vend_id);
- if (ret != CSR1212_SUCCESS) {
- csr1212_destroy_csr(host->csr.rom);
- return -ENOMEM;
- }
-
- host->update_config_rom = 1;
-
- return 0;
-}
-
-
-#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
-#include "eth1394.h"
-
-static struct csr1212_keyval *ip1394_ud;
-
-static int config_rom_ip1394_init(void)
-{
- struct csr1212_keyval *spec_id = NULL;
- struct csr1212_keyval *spec_desc = NULL;
- struct csr1212_keyval *ver = NULL;
- struct csr1212_keyval *ver_desc = NULL;
- int ret = -ENOMEM;
-
- ip1394_ud = csr1212_new_directory(CSR1212_KV_ID_UNIT);
-
- spec_id = csr1212_new_immediate(CSR1212_KV_ID_SPECIFIER_ID,
- ETHER1394_GASP_SPECIFIER_ID);
- spec_desc = csr1212_new_string_descriptor_leaf("IANA");
- ver = csr1212_new_immediate(CSR1212_KV_ID_VERSION,
- ETHER1394_GASP_VERSION);
- ver_desc = csr1212_new_string_descriptor_leaf("IPv4");
-
- if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc)
- goto ip1394_fail;
-
- csr1212_associate_keyval(spec_id, spec_desc);
- csr1212_associate_keyval(ver, ver_desc);
- if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id)
- == CSR1212_SUCCESS &&
- csr1212_attach_keyval_to_directory(ip1394_ud, ver)
- == CSR1212_SUCCESS)
- ret = 0;
-
-ip1394_fail:
- if (ret && ip1394_ud) {
- csr1212_release_keyval(ip1394_ud);
- ip1394_ud = NULL;
- }
-
- if (spec_id)
- csr1212_release_keyval(spec_id);
- if (spec_desc)
- csr1212_release_keyval(spec_desc);
- if (ver)
- csr1212_release_keyval(ver);
- if (ver_desc)
- csr1212_release_keyval(ver_desc);
-
- return ret;
-}
-
-static void config_rom_ip1394_cleanup(void)
-{
- if (ip1394_ud) {
- csr1212_release_keyval(ip1394_ud);
- ip1394_ud = NULL;
- }
-}
-
-int hpsb_config_rom_ip1394_add(struct hpsb_host *host)
-{
- if (!ip1394_ud)
- return -ENODEV;
-
- if (csr1212_attach_keyval_to_directory(host->csr.rom->root_kv,
- ip1394_ud) != CSR1212_SUCCESS)
- return -ENOMEM;
-
- host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394;
- host->update_config_rom = 1;
- return 0;
-}
-EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add);
-
-void hpsb_config_rom_ip1394_remove(struct hpsb_host *host)
-{
- csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud);
- host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394;
- host->update_config_rom = 1;
-}
-EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove);
-
-static struct hpsb_config_rom_entry ip1394_entry = {
- .name = "ip1394",
- .init = config_rom_ip1394_init,
- .cleanup = config_rom_ip1394_cleanup,
- .flag = HPSB_CONFIG_ROM_ENTRY_IP1394,
-};
-
-#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */
-
-static struct hpsb_config_rom_entry *const config_rom_entries[] = {
-#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
- &ip1394_entry,
-#endif
-};
-
-/* Initialize all config roms */
-int hpsb_init_config_roms(void)
-{
- int i, error = 0;
-
- for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
- if (config_rom_entries[i]->init()) {
- HPSB_ERR("Failed to initialize config rom entry `%s'",
- config_rom_entries[i]->name);
- error = -1;
- }
-
- return error;
-}
-
-/* Cleanup all config roms */
-void hpsb_cleanup_config_roms(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
- config_rom_entries[i]->cleanup();
-}
diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h
deleted file mode 100644
index 1f5cd1f16c44..000000000000
--- a/drivers/ieee1394/config_roms.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef _IEEE1394_CONFIG_ROMS_H
-#define _IEEE1394_CONFIG_ROMS_H
-
-struct hpsb_host;
-
-int hpsb_default_host_entry(struct hpsb_host *host);
-int hpsb_init_config_roms(void);
-void hpsb_cleanup_config_roms(void);
-
-/* List of flags to check if a host contains a certain extra config rom
- * entry. Available in the host->config_roms member. */
-#define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001
-
-#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
-int hpsb_config_rom_ip1394_add(struct hpsb_host *host);
-void hpsb_config_rom_ip1394_remove(struct hpsb_host *host);
-#endif
-
-#endif /* _IEEE1394_CONFIG_ROMS_H */
diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c
deleted file mode 100644
index d696f69ebce5..000000000000
--- a/drivers/ieee1394/csr.c
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * CSR implementation, iso/bus manager implementation.
- *
- * Copyright (C) 1999 Andreas E. Bombe
- * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- *
- *
- * Contributions:
- *
- * Manfred Weihs <weihs@ict.tuwien.ac.at>
- * configuration ROM manipulation
- *
- */
-
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/param.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-
-#include "csr1212.h"
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394.h"
-#include "highlevel.h"
-#include "ieee1394_core.h"
-
-/* Module Parameters */
-/* this module parameter can be used to disable mapping of the FCP registers */
-
-static int fcp = 1;
-module_param(fcp, int, 0444);
-MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0).");
-
-static struct csr1212_keyval *node_cap = NULL;
-
-static void add_host(struct hpsb_host *host);
-static void remove_host(struct hpsb_host *host);
-static void host_reset(struct hpsb_host *host);
-static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, size_t length, u16 fl);
-static int write_fcp(struct hpsb_host *host, int nodeid, int dest,
- quadlet_t *data, u64 addr, size_t length, u16 flags);
-static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
- u64 addr, size_t length, u16 flags);
-static int write_regs(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, size_t length, u16 flags);
-static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl);
-static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store,
- u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl);
-static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, size_t length, u16 fl);
-static u64 allocate_addr_range(u64 size, u32 alignment, void *__host);
-static void release_addr_range(u64 addr, void *__host);
-
-static struct hpsb_highlevel csr_highlevel = {
- .name = "standard registers",
- .add_host = add_host,
- .remove_host = remove_host,
- .host_reset = host_reset,
-};
-
-static const struct hpsb_address_ops map_ops = {
- .read = read_maps,
-};
-
-static const struct hpsb_address_ops fcp_ops = {
- .write = write_fcp,
-};
-
-static const struct hpsb_address_ops reg_ops = {
- .read = read_regs,
- .write = write_regs,
- .lock = lock_regs,
- .lock64 = lock64_regs,
-};
-
-static const struct hpsb_address_ops config_rom_ops = {
- .read = read_config_rom,
-};
-
-struct csr1212_bus_ops csr_bus_ops = {
- .allocate_addr_range = allocate_addr_range,
- .release_addr = release_addr_range,
-};
-
-
-static u16 csr_crc16(unsigned *data, int length)
-{
- int check=0, i;
- int shift, sum, next=0;
-
- for (i = length; i; i--) {
- for (next = check, shift = 28; shift >= 0; shift -= 4 ) {
- sum = ((next >> 12) ^ (be32_to_cpu(*data) >> shift)) & 0xf;
- next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
- }
- check = next & 0xffff;
- data++;
- }
-
- return check;
-}
-
-static void host_reset(struct hpsb_host *host)
-{
- host->csr.state &= 0x300;
-
- host->csr.bus_manager_id = 0x3f;
- host->csr.bandwidth_available = 4915;
- host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */
- host->csr.channels_available_lo = ~0;
- host->csr.broadcast_channel = 0x80000000 | 31;
-
- if (host->is_irm) {
- if (host->driver->hw_csr_reg) {
- host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0);
- }
- }
-
- host->csr.node_ids = host->node_id << 16;
-
- if (!host->is_root) {
- /* clear cmstr bit */
- host->csr.state &= ~0x100;
- }
-
- be32_add_cpu(&host->csr.topology_map[1], 1);
- host->csr.topology_map[2] = cpu_to_be32(host->node_count << 16
- | host->selfid_count);
- host->csr.topology_map[0] =
- cpu_to_be32((host->selfid_count + 2) << 16
- | csr_crc16(host->csr.topology_map + 1,
- host->selfid_count + 2));
-
- be32_add_cpu(&host->csr.speed_map[1], 1);
- host->csr.speed_map[0] = cpu_to_be32(0x3f1 << 16
- | csr_crc16(host->csr.speed_map+1,
- 0x3f1));
-}
-
-/*
- * HI == seconds (bits 0:2)
- * LO == fractions of a second in units of 125usec (bits 19:31)
- *
- * Convert SPLIT_TIMEOUT to jiffies.
- * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms.
- */
-static inline void calculate_expire(struct csr_control *csr)
-{
- unsigned int usecs = (csr->split_timeout_hi & 7) * 1000000 +
- (csr->split_timeout_lo >> 19) * 125;
-
- csr->expire = usecs_to_jiffies(usecs > 100000 ? usecs : 100000);
- HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ);
-}
-
-
-static void add_host(struct hpsb_host *host)
-{
- struct csr1212_keyval *root;
- quadlet_t bus_info[CSR_BUS_INFO_SIZE];
-
- hpsb_register_addrspace(&csr_highlevel, host, &reg_ops,
- CSR_REGISTER_BASE,
- CSR_REGISTER_BASE + CSR_CONFIG_ROM);
- hpsb_register_addrspace(&csr_highlevel, host, &config_rom_ops,
- CSR_REGISTER_BASE + CSR_CONFIG_ROM,
- CSR_REGISTER_BASE + CSR_CONFIG_ROM_END);
- if (fcp) {
- hpsb_register_addrspace(&csr_highlevel, host, &fcp_ops,
- CSR_REGISTER_BASE + CSR_FCP_COMMAND,
- CSR_REGISTER_BASE + CSR_FCP_END);
- }
- hpsb_register_addrspace(&csr_highlevel, host, &map_ops,
- CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP,
- CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END);
- hpsb_register_addrspace(&csr_highlevel, host, &map_ops,
- CSR_REGISTER_BASE + CSR_SPEED_MAP,
- CSR_REGISTER_BASE + CSR_SPEED_MAP_END);
-
- spin_lock_init(&host->csr.lock);
-
- host->csr.state = 0;
- host->csr.node_ids = 0;
- host->csr.split_timeout_hi = 0;
- host->csr.split_timeout_lo = 800 << 19;
- calculate_expire(&host->csr);
- host->csr.cycle_time = 0;
- host->csr.bus_time = 0;
- host->csr.bus_manager_id = 0x3f;
- host->csr.bandwidth_available = 4915;
- host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */
- host->csr.channels_available_lo = ~0;
- host->csr.broadcast_channel = 0x80000000 | 31;
-
- if (host->is_irm) {
- if (host->driver->hw_csr_reg) {
- host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0);
- }
- }
-
- if (host->csr.max_rec >= 9)
- host->csr.max_rom = 2;
- else if (host->csr.max_rec >= 5)
- host->csr.max_rom = 1;
- else
- host->csr.max_rom = 0;
-
- host->csr.generation = 2;
-
- bus_info[1] = IEEE1394_BUSID_MAGIC;
- bus_info[2] = cpu_to_be32((hpsb_disable_irm ? 0 : 1 << CSR_IRMC_SHIFT) |
- (1 << CSR_CMC_SHIFT) |
- (1 << CSR_ISC_SHIFT) |
- (0 << CSR_BMC_SHIFT) |
- (0 << CSR_PMC_SHIFT) |
- (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) |
- (host->csr.max_rec << CSR_MAX_REC_SHIFT) |
- (host->csr.max_rom << CSR_MAX_ROM_SHIFT) |
- (host->csr.generation << CSR_GENERATION_SHIFT) |
- host->csr.lnk_spd);
-
- bus_info[3] = cpu_to_be32(host->csr.guid_hi);
- bus_info[4] = cpu_to_be32(host->csr.guid_lo);
-
- /* The hardware copy of the bus info block will be set later when a
- * bus reset is issued. */
-
- csr1212_init_local_csr(host->csr.rom, bus_info, host->csr.max_rom);
-
- root = host->csr.rom->root_kv;
-
- if(csr1212_attach_keyval_to_directory(root, node_cap) != CSR1212_SUCCESS) {
- HPSB_ERR("Failed to attach Node Capabilities to root directory");
- }
-
- host->update_config_rom = 1;
-}
-
-static void remove_host(struct hpsb_host *host)
-{
- quadlet_t bus_info[CSR_BUS_INFO_SIZE];
-
- bus_info[1] = IEEE1394_BUSID_MAGIC;
- bus_info[2] = cpu_to_be32((0 << CSR_IRMC_SHIFT) |
- (0 << CSR_CMC_SHIFT) |
- (0 << CSR_ISC_SHIFT) |
- (0 << CSR_BMC_SHIFT) |
- (0 << CSR_PMC_SHIFT) |
- (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) |
- (host->csr.max_rec << CSR_MAX_REC_SHIFT) |
- (0 << CSR_MAX_ROM_SHIFT) |
- (0 << CSR_GENERATION_SHIFT) |
- host->csr.lnk_spd);
-
- bus_info[3] = cpu_to_be32(host->csr.guid_hi);
- bus_info[4] = cpu_to_be32(host->csr.guid_lo);
-
- csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, node_cap);
-
- csr1212_init_local_csr(host->csr.rom, bus_info, 0);
- host->update_config_rom = 1;
-}
-
-
-int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
- size_t buffersize, unsigned char rom_version)
-{
- unsigned long flags;
- int ret;
-
- HPSB_NOTICE("hpsb_update_config_rom() is deprecated");
-
- spin_lock_irqsave(&host->csr.lock, flags);
- if (rom_version != host->csr.generation)
- ret = -1;
- else if (buffersize > host->csr.rom->cache_head->size)
- ret = -2;
- else {
- /* Just overwrite the generated ConfigROM image with new data,
- * it can be regenerated later. */
- memcpy(host->csr.rom->cache_head->data, new_rom, buffersize);
- host->csr.rom->cache_head->len = buffersize;
-
- if (host->driver->set_hw_config_rom)
- host->driver->set_hw_config_rom(host, host->csr.rom->bus_info_data);
- /* Increment the generation number to keep some sort of sync
- * with the newer ConfigROM manipulation method. */
- host->csr.generation++;
- if (host->csr.generation > 0xf || host->csr.generation < 2)
- host->csr.generation = 2;
- ret=0;
- }
- spin_unlock_irqrestore(&host->csr.lock, flags);
- return ret;
-}
-
-
-/* Read topology / speed maps and configuration ROM */
-static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, size_t length, u16 fl)
-{
- unsigned long flags;
- int csraddr = addr - CSR_REGISTER_BASE;
- const char *src;
-
- spin_lock_irqsave(&host->csr.lock, flags);
-
- if (csraddr < CSR_SPEED_MAP) {
- src = ((char *)host->csr.topology_map) + csraddr
- - CSR_TOPOLOGY_MAP;
- } else {
- src = ((char *)host->csr.speed_map) + csraddr - CSR_SPEED_MAP;
- }
-
- memcpy(buffer, src, length);
- spin_unlock_irqrestore(&host->csr.lock, flags);
- return RCODE_COMPLETE;
-}
-
-
-#define out if (--length == 0) break
-
-static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
- u64 addr, size_t length, u16 flags)
-{
- int csraddr = addr - CSR_REGISTER_BASE;
- int oldcycle;
- quadlet_t ret;
-
- if ((csraddr | length) & 0x3)
- return RCODE_TYPE_ERROR;
-
- length /= 4;
-
- switch (csraddr) {
- case CSR_STATE_CLEAR:
- *(buf++) = cpu_to_be32(host->csr.state);
- out;
- case CSR_STATE_SET:
- *(buf++) = cpu_to_be32(host->csr.state);
- out;
- case CSR_NODE_IDS:
- *(buf++) = cpu_to_be32(host->csr.node_ids);
- out;
-
- case CSR_RESET_START:
- return RCODE_TYPE_ERROR;
-
- /* address gap - handled by default below */
-
- case CSR_SPLIT_TIMEOUT_HI:
- *(buf++) = cpu_to_be32(host->csr.split_timeout_hi);
- out;
- case CSR_SPLIT_TIMEOUT_LO:
- *(buf++) = cpu_to_be32(host->csr.split_timeout_lo);
- out;
-
- /* address gap */
- return RCODE_ADDRESS_ERROR;
-
- case CSR_CYCLE_TIME:
- oldcycle = host->csr.cycle_time;
- host->csr.cycle_time =
- host->driver->devctl(host, GET_CYCLE_COUNTER, 0);
-
- if (oldcycle > host->csr.cycle_time) {
- /* cycle time wrapped around */
- host->csr.bus_time += 1 << 7;
- }
- *(buf++) = cpu_to_be32(host->csr.cycle_time);
- out;
- case CSR_BUS_TIME:
- oldcycle = host->csr.cycle_time;
- host->csr.cycle_time =
- host->driver->devctl(host, GET_CYCLE_COUNTER, 0);
-
- if (oldcycle > host->csr.cycle_time) {
- /* cycle time wrapped around */
- host->csr.bus_time += (1 << 7);
- }
- *(buf++) = cpu_to_be32(host->csr.bus_time
- | (host->csr.cycle_time >> 25));
- out;
-
- /* address gap */
- return RCODE_ADDRESS_ERROR;
-
- case CSR_BUSY_TIMEOUT:
- /* not yet implemented */
- return RCODE_ADDRESS_ERROR;
-
- case CSR_BUS_MANAGER_ID:
- if (host->driver->hw_csr_reg)
- ret = host->driver->hw_csr_reg(host, 0, 0, 0);
- else
- ret = host->csr.bus_manager_id;
-
- *(buf++) = cpu_to_be32(ret);
- out;
- case CSR_BANDWIDTH_AVAILABLE:
- if (host->driver->hw_csr_reg)
- ret = host->driver->hw_csr_reg(host, 1, 0, 0);
- else
- ret = host->csr.bandwidth_available;
-
- *(buf++) = cpu_to_be32(ret);
- out;
- case CSR_CHANNELS_AVAILABLE_HI:
- if (host->driver->hw_csr_reg)
- ret = host->driver->hw_csr_reg(host, 2, 0, 0);
- else
- ret = host->csr.channels_available_hi;
-
- *(buf++) = cpu_to_be32(ret);
- out;
- case CSR_CHANNELS_AVAILABLE_LO:
- if (host->driver->hw_csr_reg)
- ret = host->driver->hw_csr_reg(host, 3, 0, 0);
- else
- ret = host->csr.channels_available_lo;
-
- *(buf++) = cpu_to_be32(ret);
- out;
-
- case CSR_BROADCAST_CHANNEL:
- *(buf++) = cpu_to_be32(host->csr.broadcast_channel);
- out;
-
- /* address gap to end - fall through to default */
- default:
- return RCODE_ADDRESS_ERROR;
- }
-
- return RCODE_COMPLETE;
-}
-
-static int write_regs(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, size_t length, u16 flags)
-{
- int csraddr = addr - CSR_REGISTER_BASE;
-
- if ((csraddr | length) & 0x3)
- return RCODE_TYPE_ERROR;
-
- length /= 4;
-
- switch (csraddr) {
- case CSR_STATE_CLEAR:
- /* FIXME FIXME FIXME */
- printk("doh, someone wants to mess with state clear\n");
- out;
- case CSR_STATE_SET:
- printk("doh, someone wants to mess with state set\n");
- out;
-
- case CSR_NODE_IDS:
- host->csr.node_ids &= NODE_MASK << 16;
- host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16);
- host->node_id = host->csr.node_ids >> 16;
- host->driver->devctl(host, SET_BUS_ID, host->node_id >> 6);
- out;
-
- case CSR_RESET_START:
- /* FIXME - perform command reset */
- out;
-
- /* address gap */
- return RCODE_ADDRESS_ERROR;
-
- case CSR_SPLIT_TIMEOUT_HI:
- host->csr.split_timeout_hi =
- be32_to_cpu(*(data++)) & 0x00000007;
- calculate_expire(&host->csr);
- out;
- case CSR_SPLIT_TIMEOUT_LO:
- host->csr.split_timeout_lo =
- be32_to_cpu(*(data++)) & 0xfff80000;
- calculate_expire(&host->csr);
- out;
-
- /* address gap */
- return RCODE_ADDRESS_ERROR;
-
- case CSR_CYCLE_TIME:
- /* should only be set by cycle start packet, automatically */
- host->csr.cycle_time = be32_to_cpu(*data);
- host->driver->devctl(host, SET_CYCLE_COUNTER,
- be32_to_cpu(*(data++)));
- out;
- case CSR_BUS_TIME:
- host->csr.bus_time = be32_to_cpu(*(data++)) & 0xffffff80;
- out;
-
- /* address gap */
- return RCODE_ADDRESS_ERROR;
-
- case CSR_BUSY_TIMEOUT:
- /* not yet implemented */
- return RCODE_ADDRESS_ERROR;
-
- case CSR_BUS_MANAGER_ID:
- case CSR_BANDWIDTH_AVAILABLE:
- case CSR_CHANNELS_AVAILABLE_HI:
- case CSR_CHANNELS_AVAILABLE_LO:
- /* these are not writable, only lockable */
- return RCODE_TYPE_ERROR;
-
- case CSR_BROADCAST_CHANNEL:
- /* only the valid bit can be written */
- host->csr.broadcast_channel = (host->csr.broadcast_channel & ~0x40000000)
- | (be32_to_cpu(*data) & 0x40000000);
- out;
-
- /* address gap to end - fall through */
- default:
- return RCODE_ADDRESS_ERROR;
- }
-
- return RCODE_COMPLETE;
-}
-
-#undef out
-
-
-static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl)
-{
- int csraddr = addr - CSR_REGISTER_BASE;
- unsigned long flags;
- quadlet_t *regptr = NULL;
-
- if (csraddr & 0x3)
- return RCODE_TYPE_ERROR;
-
- if (csraddr < CSR_BUS_MANAGER_ID || csraddr > CSR_CHANNELS_AVAILABLE_LO
- || extcode != EXTCODE_COMPARE_SWAP)
- goto unsupported_lockreq;
-
- data = be32_to_cpu(data);
- arg = be32_to_cpu(arg);
-
- /* Is somebody releasing the broadcast_channel on us? */
- if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x1)) {
- /* Note: this is may not be the right way to handle
- * the problem, so we should look into the proper way
- * eventually. */
- HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release "
- "broadcast channel 31. Ignoring.",
- NODE_BUS_ARGS(host, nodeid));
-
- data &= ~0x1; /* keep broadcast channel allocated */
- }
-
- if (host->driver->hw_csr_reg) {
- quadlet_t old;
-
- old = host->driver->
- hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2,
- data, arg);
-
- *store = cpu_to_be32(old);
- return RCODE_COMPLETE;
- }
-
- spin_lock_irqsave(&host->csr.lock, flags);
-
- switch (csraddr) {
- case CSR_BUS_MANAGER_ID:
- regptr = &host->csr.bus_manager_id;
- *store = cpu_to_be32(*regptr);
- if (*regptr == arg)
- *regptr = data;
- break;
-
- case CSR_BANDWIDTH_AVAILABLE:
- {
- quadlet_t bandwidth;
- quadlet_t old;
- quadlet_t new;
-
- regptr = &host->csr.bandwidth_available;
- old = *regptr;
-
- /* bandwidth available algorithm adapted from IEEE 1394a-2000 spec */
- if (arg > 0x1fff) {
- *store = cpu_to_be32(old); /* change nothing */
- break;
- }
- data &= 0x1fff;
- if (arg >= data) {
- /* allocate bandwidth */
- bandwidth = arg - data;
- if (old >= bandwidth) {
- new = old - bandwidth;
- *store = cpu_to_be32(arg);
- *regptr = new;
- } else {
- *store = cpu_to_be32(old);
- }
- } else {
- /* deallocate bandwidth */
- bandwidth = data - arg;
- if (old + bandwidth < 0x2000) {
- new = old + bandwidth;
- *store = cpu_to_be32(arg);
- *regptr = new;
- } else {
- *store = cpu_to_be32(old);
- }
- }
- break;
- }
-
- case CSR_CHANNELS_AVAILABLE_HI:
- {
- /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */
- quadlet_t affected_channels = arg ^ data;
-
- regptr = &host->csr.channels_available_hi;
-
- if ((arg & affected_channels) == (*regptr & affected_channels)) {
- *regptr ^= affected_channels;
- *store = cpu_to_be32(arg);
- } else {
- *store = cpu_to_be32(*regptr);
- }
-
- break;
- }
-
- case CSR_CHANNELS_AVAILABLE_LO:
- {
- /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */
- quadlet_t affected_channels = arg ^ data;
-
- regptr = &host->csr.channels_available_lo;
-
- if ((arg & affected_channels) == (*regptr & affected_channels)) {
- *regptr ^= affected_channels;
- *store = cpu_to_be32(arg);
- } else {
- *store = cpu_to_be32(*regptr);
- }
- break;
- }
- }
-
- spin_unlock_irqrestore(&host->csr.lock, flags);
-
- return RCODE_COMPLETE;
-
- unsupported_lockreq:
- switch (csraddr) {
- case CSR_STATE_CLEAR:
- case CSR_STATE_SET:
- case CSR_RESET_START:
- case CSR_NODE_IDS:
- case CSR_SPLIT_TIMEOUT_HI:
- case CSR_SPLIT_TIMEOUT_LO:
- case CSR_CYCLE_TIME:
- case CSR_BUS_TIME:
- case CSR_BROADCAST_CHANNEL:
- return RCODE_TYPE_ERROR;
-
- case CSR_BUSY_TIMEOUT:
- /* not yet implemented - fall through */
- default:
- return RCODE_ADDRESS_ERROR;
- }
-}
-
-static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store,
- u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl)
-{
- int csraddr = addr - CSR_REGISTER_BASE;
- unsigned long flags;
-
- data = be64_to_cpu(data);
- arg = be64_to_cpu(arg);
-
- if (csraddr & 0x3)
- return RCODE_TYPE_ERROR;
-
- if (csraddr != CSR_CHANNELS_AVAILABLE
- || extcode != EXTCODE_COMPARE_SWAP)
- goto unsupported_lock64req;
-
- /* Is somebody releasing the broadcast_channel on us? */
- if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x100000000ULL)) {
- /* Note: this is may not be the right way to handle
- * the problem, so we should look into the proper way
- * eventually. */
- HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release "
- "broadcast channel 31. Ignoring.",
- NODE_BUS_ARGS(host, nodeid));
-
- data &= ~0x100000000ULL; /* keep broadcast channel allocated */
- }
-
- if (host->driver->hw_csr_reg) {
- quadlet_t data_hi, data_lo;
- quadlet_t arg_hi, arg_lo;
- quadlet_t old_hi, old_lo;
-
- data_hi = data >> 32;
- data_lo = data & 0xFFFFFFFF;
- arg_hi = arg >> 32;
- arg_lo = arg & 0xFFFFFFFF;
-
- old_hi = host->driver->hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2,
- data_hi, arg_hi);
-
- old_lo = host->driver->hw_csr_reg(host, ((csraddr + 4) - CSR_BUS_MANAGER_ID) >> 2,
- data_lo, arg_lo);
-
- *store = cpu_to_be64(((octlet_t)old_hi << 32) | old_lo);
- } else {
- octlet_t old;
- octlet_t affected_channels = arg ^ data;
-
- spin_lock_irqsave(&host->csr.lock, flags);
-
- old = ((octlet_t)host->csr.channels_available_hi << 32) | host->csr.channels_available_lo;
-
- if ((arg & affected_channels) == (old & affected_channels)) {
- host->csr.channels_available_hi ^= (affected_channels >> 32);
- host->csr.channels_available_lo ^= (affected_channels & 0xffffffff);
- *store = cpu_to_be64(arg);
- } else {
- *store = cpu_to_be64(old);
- }
-
- spin_unlock_irqrestore(&host->csr.lock, flags);
- }
-
- /* Is somebody erroneously releasing the broadcast_channel on us? */
- if (host->csr.channels_available_hi & 0x1)
- host->csr.channels_available_hi &= ~0x1;
-
- return RCODE_COMPLETE;
-
- unsupported_lock64req:
- switch (csraddr) {
- case CSR_STATE_CLEAR:
- case CSR_STATE_SET:
- case CSR_RESET_START:
- case CSR_NODE_IDS:
- case CSR_SPLIT_TIMEOUT_HI:
- case CSR_SPLIT_TIMEOUT_LO:
- case CSR_CYCLE_TIME:
- case CSR_BUS_TIME:
- case CSR_BUS_MANAGER_ID:
- case CSR_BROADCAST_CHANNEL:
- case CSR_BUSY_TIMEOUT:
- case CSR_BANDWIDTH_AVAILABLE:
- return RCODE_TYPE_ERROR;
-
- default:
- return RCODE_ADDRESS_ERROR;
- }
-}
-
-static int write_fcp(struct hpsb_host *host, int nodeid, int dest,
- quadlet_t *data, u64 addr, size_t length, u16 flags)
-{
- int csraddr = addr - CSR_REGISTER_BASE;
-
- if (length > 512)
- return RCODE_TYPE_ERROR;
-
- switch (csraddr) {
- case CSR_FCP_COMMAND:
- highlevel_fcp_request(host, nodeid, 0, (u8 *)data, length);
- break;
- case CSR_FCP_RESPONSE:
- highlevel_fcp_request(host, nodeid, 1, (u8 *)data, length);
- break;
- default:
- return RCODE_TYPE_ERROR;
- }
-
- return RCODE_COMPLETE;
-}
-
-static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, size_t length, u16 fl)
-{
- u32 offset = addr - CSR1212_REGISTER_SPACE_BASE;
-
- if (csr1212_read(host->csr.rom, offset, buffer, length) == CSR1212_SUCCESS)
- return RCODE_COMPLETE;
- else
- return RCODE_ADDRESS_ERROR;
-}
-
-static u64 allocate_addr_range(u64 size, u32 alignment, void *__host)
-{
- struct hpsb_host *host = (struct hpsb_host*)__host;
-
- return hpsb_allocate_and_register_addrspace(&csr_highlevel,
- host,
- &config_rom_ops,
- size, alignment,
- CSR1212_UNITS_SPACE_BASE,
- CSR1212_UNITS_SPACE_END);
-}
-
-static void release_addr_range(u64 addr, void *__host)
-{
- struct hpsb_host *host = (struct hpsb_host*)__host;
- hpsb_unregister_addrspace(&csr_highlevel, host, addr);
-}
-
-
-int init_csr(void)
-{
- node_cap = csr1212_new_immediate(CSR1212_KV_ID_NODE_CAPABILITIES, 0x0083c0);
- if (!node_cap) {
- HPSB_ERR("Failed to allocate memory for Node Capabilties ConfigROM entry!");
- return -ENOMEM;
- }
-
- hpsb_register_highlevel(&csr_highlevel);
-
- return 0;
-}
-
-void cleanup_csr(void)
-{
- if (node_cap)
- csr1212_release_keyval(node_cap);
- hpsb_unregister_highlevel(&csr_highlevel);
-}
diff --git a/drivers/ieee1394/csr.h b/drivers/ieee1394/csr.h
deleted file mode 100644
index 90fb3f2192c3..000000000000
--- a/drivers/ieee1394/csr.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef _IEEE1394_CSR_H
-#define _IEEE1394_CSR_H
-
-#include <linux/spinlock_types.h>
-
-#include "csr1212.h"
-#include "ieee1394_types.h"
-
-#define CSR_REGISTER_BASE 0xfffff0000000ULL
-
-/* register offsets relative to CSR_REGISTER_BASE */
-#define CSR_STATE_CLEAR 0x0
-#define CSR_STATE_SET 0x4
-#define CSR_NODE_IDS 0x8
-#define CSR_RESET_START 0xc
-#define CSR_SPLIT_TIMEOUT_HI 0x18
-#define CSR_SPLIT_TIMEOUT_LO 0x1c
-#define CSR_CYCLE_TIME 0x200
-#define CSR_BUS_TIME 0x204
-#define CSR_BUSY_TIMEOUT 0x210
-#define CSR_BUS_MANAGER_ID 0x21c
-#define CSR_BANDWIDTH_AVAILABLE 0x220
-#define CSR_CHANNELS_AVAILABLE 0x224
-#define CSR_CHANNELS_AVAILABLE_HI 0x224
-#define CSR_CHANNELS_AVAILABLE_LO 0x228
-#define CSR_BROADCAST_CHANNEL 0x234
-#define CSR_CONFIG_ROM 0x400
-#define CSR_CONFIG_ROM_END 0x800
-#define CSR_FCP_COMMAND 0xB00
-#define CSR_FCP_RESPONSE 0xD00
-#define CSR_FCP_END 0xF00
-#define CSR_TOPOLOGY_MAP 0x1000
-#define CSR_TOPOLOGY_MAP_END 0x1400
-#define CSR_SPEED_MAP 0x2000
-#define CSR_SPEED_MAP_END 0x3000
-
-/* IEEE 1394 bus specific Configuration ROM Key IDs */
-#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30)
-
-/* IEEE 1394 Bus Information Block specifics */
-#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t))
-
-#define CSR_IRMC_SHIFT 31
-#define CSR_CMC_SHIFT 30
-#define CSR_ISC_SHIFT 29
-#define CSR_BMC_SHIFT 28
-#define CSR_PMC_SHIFT 27
-#define CSR_CYC_CLK_ACC_SHIFT 16
-#define CSR_MAX_REC_SHIFT 12
-#define CSR_MAX_ROM_SHIFT 8
-#define CSR_GENERATION_SHIFT 4
-
-static inline void csr_set_bus_info_generation(struct csr1212_csr *csr, u8 gen)
-{
- csr->bus_info_data[2] &= ~cpu_to_be32(0xf << CSR_GENERATION_SHIFT);
- csr->bus_info_data[2] |= cpu_to_be32((u32)gen << CSR_GENERATION_SHIFT);
-}
-
-struct csr_control {
- spinlock_t lock;
-
- quadlet_t state;
- quadlet_t node_ids;
- quadlet_t split_timeout_hi, split_timeout_lo;
- unsigned long expire; /* Calculated from split_timeout */
- quadlet_t cycle_time;
- quadlet_t bus_time;
- quadlet_t bus_manager_id;
- quadlet_t bandwidth_available;
- quadlet_t channels_available_hi, channels_available_lo;
- quadlet_t broadcast_channel;
-
- /* Bus Info */
- quadlet_t guid_hi, guid_lo;
- u8 cyc_clk_acc;
- u8 max_rec;
- u8 max_rom;
- u8 generation; /* Only use values between 0x2 and 0xf */
- u8 lnk_spd;
-
- unsigned long gen_timestamp[16];
-
- struct csr1212_csr *rom;
-
- quadlet_t topology_map[256];
- quadlet_t speed_map[1024];
-};
-
-extern struct csr1212_bus_ops csr_bus_ops;
-
-int init_csr(void);
-void cleanup_csr(void);
-
-/* hpsb_update_config_rom() is deprecated */
-struct hpsb_host;
-int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
- size_t size, unsigned char rom_version);
-
-#endif /* _IEEE1394_CSR_H */
diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c
deleted file mode 100644
index e76cac64c533..000000000000
--- a/drivers/ieee1394/csr1212.c
+++ /dev/null
@@ -1,1467 +0,0 @@
-/*
- * csr1212.c -- IEEE 1212 Control and Status Register support for Linux
- *
- * Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za>
- * Steve Kinneberg <kinnebergsteve@acmsystems.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
- */
-
-
-/* TODO List:
- * - Verify interface consistency: i.e., public functions that take a size
- * parameter expect size to be in bytes.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/kmemcheck.h>
-#include <linux/string.h>
-#include <asm/bug.h>
-#include <asm/byteorder.h>
-
-#include "csr1212.h"
-
-
-/* Permitted key type for each key id */
-#define __I (1 << CSR1212_KV_TYPE_IMMEDIATE)
-#define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET)
-#define __D (1 << CSR1212_KV_TYPE_DIRECTORY)
-#define __L (1 << CSR1212_KV_TYPE_LEAF)
-static const u8 csr1212_key_id_type_map[0x30] = {
- __C, /* used by Apple iSight */
- __D | __L, /* Descriptor */
- __I | __D | __L, /* Bus_Dependent_Info */
- __I | __D | __L, /* Vendor */
- __I, /* Hardware_Version */
- 0, 0, /* Reserved */
- __D | __L | __I, /* Module */
- __I, 0, 0, 0, /* used by Apple iSight, Reserved */
- __I, /* Node_Capabilities */
- __L, /* EUI_64 */
- 0, 0, 0, /* Reserved */
- __D, /* Unit */
- __I, /* Specifier_ID */
- __I, /* Version */
- __I | __C | __D | __L, /* Dependent_Info */
- __L, /* Unit_Location */
- 0, /* Reserved */
- __I, /* Model */
- __D, /* Instance */
- __L, /* Keyword */
- __D, /* Feature */
- __L, /* Extended_ROM */
- __I, /* Extended_Key_Specifier_ID */
- __I, /* Extended_Key */
- __I | __C | __D | __L, /* Extended_Data */
- __L, /* Modifiable_Descriptor */
- __I, /* Directory_ID */
- __I, /* Revision */
-};
-#undef __I
-#undef __C
-#undef __D
-#undef __L
-
-
-#define quads_to_bytes(_q) ((_q) * sizeof(u32))
-#define bytes_to_quads(_b) DIV_ROUND_UP(_b, sizeof(u32))
-
-static void free_keyval(struct csr1212_keyval *kv)
-{
- if ((kv->key.type == CSR1212_KV_TYPE_LEAF) &&
- (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM))
- CSR1212_FREE(kv->value.leaf.data);
-
- CSR1212_FREE(kv);
-}
-
-static u16 csr1212_crc16(const u32 *buffer, size_t length)
-{
- int shift;
- u32 data;
- u16 sum, crc = 0;
-
- for (; length; length--) {
- data = be32_to_cpu(*buffer);
- buffer++;
- for (shift = 28; shift >= 0; shift -= 4 ) {
- sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
- crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
- }
- crc &= 0xffff;
- }
-
- return cpu_to_be16(crc);
-}
-
-/* Microsoft computes the CRC with the bytes in reverse order. */
-static u16 csr1212_msft_crc16(const u32 *buffer, size_t length)
-{
- int shift;
- u32 data;
- u16 sum, crc = 0;
-
- for (; length; length--) {
- data = le32_to_cpu(*buffer);
- buffer++;
- for (shift = 28; shift >= 0; shift -= 4 ) {
- sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
- crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
- }
- crc &= 0xffff;
- }
-
- return cpu_to_be16(crc);
-}
-
-static struct csr1212_dentry *
-csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv)
-{
- struct csr1212_dentry *pos;
-
- for (pos = dir->value.directory.dentries_head;
- pos != NULL; pos = pos->next)
- if (pos->kv == kv)
- return pos;
- return NULL;
-}
-
-static struct csr1212_keyval *
-csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset)
-{
- struct csr1212_keyval *kv;
-
- for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next)
- if (kv->offset == offset)
- return kv;
- return NULL;
-}
-
-
-/* Creation Routines */
-
-struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops,
- size_t bus_info_size, void *private)
-{
- struct csr1212_csr *csr;
-
- csr = CSR1212_MALLOC(sizeof(*csr));
- if (!csr)
- return NULL;
-
- csr->cache_head =
- csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET,
- CSR1212_CONFIG_ROM_SPACE_SIZE);
- if (!csr->cache_head) {
- CSR1212_FREE(csr);
- return NULL;
- }
-
- /* The keyval key id is not used for the root node, but a valid key id
- * that can be used for a directory needs to be passed to
- * csr1212_new_directory(). */
- csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR);
- if (!csr->root_kv) {
- CSR1212_FREE(csr->cache_head);
- CSR1212_FREE(csr);
- return NULL;
- }
-
- csr->bus_info_data = csr->cache_head->data;
- csr->bus_info_len = bus_info_size;
- csr->crc_len = bus_info_size;
- csr->ops = ops;
- csr->private = private;
- csr->cache_tail = csr->cache_head;
-
- return csr;
-}
-
-void csr1212_init_local_csr(struct csr1212_csr *csr,
- const u32 *bus_info_data, int max_rom)
-{
- static const int mr_map[] = { 4, 64, 1024, 0 };
-
- BUG_ON(max_rom & ~0x3);
- csr->max_rom = mr_map[max_rom];
- memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len);
-}
-
-static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key)
-{
- struct csr1212_keyval *kv;
-
- if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0))
- return NULL;
-
- kv = CSR1212_MALLOC(sizeof(*kv));
- if (!kv)
- return NULL;
-
- atomic_set(&kv->refcnt, 1);
- kv->key.type = type;
- kv->key.id = key;
- kv->associate = NULL;
- kv->next = NULL;
- kv->prev = NULL;
- kv->offset = 0;
- kv->valid = 0;
- return kv;
-}
-
-struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value)
-{
- struct csr1212_keyval *kv;
-
- kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key);
- if (!kv)
- return NULL;
-
- kv->value.immediate = value;
- kv->valid = 1;
- return kv;
-}
-
-static struct csr1212_keyval *
-csr1212_new_leaf(u8 key, const void *data, size_t data_len)
-{
- struct csr1212_keyval *kv;
-
- kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key);
- if (!kv)
- return NULL;
-
- if (data_len > 0) {
- kv->value.leaf.data = CSR1212_MALLOC(data_len);
- if (!kv->value.leaf.data) {
- CSR1212_FREE(kv);
- return NULL;
- }
-
- if (data)
- memcpy(kv->value.leaf.data, data, data_len);
- } else {
- kv->value.leaf.data = NULL;
- }
-
- kv->value.leaf.len = bytes_to_quads(data_len);
- kv->offset = 0;
- kv->valid = 1;
-
- return kv;
-}
-
-static struct csr1212_keyval *
-csr1212_new_csr_offset(u8 key, u32 csr_offset)
-{
- struct csr1212_keyval *kv;
-
- kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key);
- if (!kv)
- return NULL;
-
- kv->value.csr_offset = csr_offset;
-
- kv->offset = 0;
- kv->valid = 1;
- return kv;
-}
-
-struct csr1212_keyval *csr1212_new_directory(u8 key)
-{
- struct csr1212_keyval *kv;
-
- kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key);
- if (!kv)
- return NULL;
-
- kv->value.directory.len = 0;
- kv->offset = 0;
- kv->value.directory.dentries_head = NULL;
- kv->value.directory.dentries_tail = NULL;
- kv->valid = 1;
- return kv;
-}
-
-void csr1212_associate_keyval(struct csr1212_keyval *kv,
- struct csr1212_keyval *associate)
-{
- BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR ||
- (associate->key.id != CSR1212_KV_ID_DESCRIPTOR &&
- associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO &&
- associate->key.id != CSR1212_KV_ID_EXTENDED_KEY &&
- associate->key.id != CSR1212_KV_ID_EXTENDED_DATA &&
- associate->key.id < 0x30) ||
- (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID &&
- associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) ||
- (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
- associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) ||
- (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
- kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) ||
- (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA &&
- kv->key.id != CSR1212_KV_ID_EXTENDED_KEY));
-
- if (kv->associate)
- csr1212_release_keyval(kv->associate);
-
- csr1212_keep_keyval(associate);
- kv->associate = associate;
-}
-
-static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
- struct csr1212_keyval *kv,
- bool keep_keyval)
-{
- struct csr1212_dentry *dentry;
-
- BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY);
-
- dentry = CSR1212_MALLOC(sizeof(*dentry));
- if (!dentry)
- return -ENOMEM;
-
- if (keep_keyval)
- csr1212_keep_keyval(kv);
- dentry->kv = kv;
-
- dentry->next = NULL;
- dentry->prev = dir->value.directory.dentries_tail;
-
- if (!dir->value.directory.dentries_head)
- dir->value.directory.dentries_head = dentry;
-
- if (dir->value.directory.dentries_tail)
- dir->value.directory.dentries_tail->next = dentry;
- dir->value.directory.dentries_tail = dentry;
-
- return CSR1212_SUCCESS;
-}
-
-int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
- struct csr1212_keyval *kv)
-{
- return __csr1212_attach_keyval_to_directory(dir, kv, true);
-}
-
-#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \
- (&((kv)->value.leaf.data[1]))
-
-#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \
- ((kv)->value.leaf.data[0] = \
- cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \
- ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)))
-#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \
- ((kv)->value.leaf.data[0] = \
- cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \
- CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \
- ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)))
-
-static struct csr1212_keyval *
-csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id,
- const void *data, size_t data_len)
-{
- struct csr1212_keyval *kv;
-
- kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL,
- data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD);
- if (!kv)
- return NULL;
-
- kmemcheck_annotate_variable(kv->value.leaf.data[0]);
- CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype);
- CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id);
-
- if (data)
- memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len);
-
- return kv;
-}
-
-/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */
-static int csr1212_check_minimal_ascii(const char *s)
-{
- static const char minimal_ascii_table[] = {
- /* 1 2 4 8 16 32 64 128 */
- 128, /* --, --, --, --, --, --, --, 07, */
- 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */
- 0, /* --, --, --, --, --, --, --, --, */
- 0, /* --, --, --, --, --, --, --, --, */
- 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */
- 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */
- 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */
- 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */
- 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */
- 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */
- 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */
- 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */
- 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */
- 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */
- 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */
- 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */
- };
- int i, j;
-
- for (; *s; s++) {
- i = *s >> 3; /* i = *s / 8; */
- j = 1 << (*s & 3); /* j = 1 << (*s % 8); */
-
- if (i >= ARRAY_SIZE(minimal_ascii_table) ||
- !(minimal_ascii_table[i] & j))
- return -EINVAL;
- }
- return 0;
-}
-
-/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */
-struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s)
-{
- struct csr1212_keyval *kv;
- u32 *text;
- size_t str_len, quads;
-
- if (!s || !*s || csr1212_check_minimal_ascii(s))
- return NULL;
-
- str_len = strlen(s);
- quads = bytes_to_quads(str_len);
- kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) +
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD);
- if (!kv)
- return NULL;
-
- kv->value.leaf.data[1] = 0; /* width, character_set, language */
- text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv);
- text[quads - 1] = 0; /* padding */
- memcpy(text, s, str_len);
-
- return kv;
-}
-
-
-/* Destruction Routines */
-
-void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir,
- struct csr1212_keyval *kv)
-{
- struct csr1212_dentry *dentry;
-
- if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY)
- return;
-
- dentry = csr1212_find_keyval(dir, kv);
-
- if (!dentry)
- return;
-
- if (dentry->prev)
- dentry->prev->next = dentry->next;
- if (dentry->next)
- dentry->next->prev = dentry->prev;
- if (dir->value.directory.dentries_head == dentry)
- dir->value.directory.dentries_head = dentry->next;
- if (dir->value.directory.dentries_tail == dentry)
- dir->value.directory.dentries_tail = dentry->prev;
-
- CSR1212_FREE(dentry);
-
- csr1212_release_keyval(kv);
-}
-
-/* This function is used to free the memory taken by a keyval. If the given
- * keyval is a directory type, then any keyvals contained in that directory
- * will be destroyed as well if noone holds a reference on them. By means of
- * list manipulation, this routine will descend a directory structure in a
- * non-recursive manner. */
-void csr1212_release_keyval(struct csr1212_keyval *kv)
-{
- struct csr1212_keyval *k, *a;
- struct csr1212_dentry dentry;
- struct csr1212_dentry *head, *tail;
-
- if (!atomic_dec_and_test(&kv->refcnt))
- return;
-
- dentry.kv = kv;
- dentry.next = NULL;
- dentry.prev = NULL;
-
- head = &dentry;
- tail = head;
-
- while (head) {
- k = head->kv;
-
- while (k) {
- /* must not dec_and_test kv->refcnt again */
- if (k != kv && !atomic_dec_and_test(&k->refcnt))
- break;
-
- a = k->associate;
-
- if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) {
- /* If the current entry is a directory, move all
- * the entries to the destruction list. */
- if (k->value.directory.dentries_head) {
- tail->next =
- k->value.directory.dentries_head;
- k->value.directory.dentries_head->prev =
- tail;
- tail = k->value.directory.dentries_tail;
- }
- }
- free_keyval(k);
- k = a;
- }
-
- head = head->next;
- if (head) {
- if (head->prev && head->prev != &dentry)
- CSR1212_FREE(head->prev);
- head->prev = NULL;
- } else if (tail != &dentry) {
- CSR1212_FREE(tail);
- }
- }
-}
-
-void csr1212_destroy_csr(struct csr1212_csr *csr)
-{
- struct csr1212_csr_rom_cache *c, *oc;
- struct csr1212_cache_region *cr, *ocr;
-
- csr1212_release_keyval(csr->root_kv);
-
- c = csr->cache_head;
- while (c) {
- oc = c;
- cr = c->filled_head;
- while (cr) {
- ocr = cr;
- cr = cr->next;
- CSR1212_FREE(ocr);
- }
- c = c->next;
- CSR1212_FREE(oc);
- }
-
- CSR1212_FREE(csr);
-}
-
-
-/* CSR Image Creation */
-
-static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize)
-{
- struct csr1212_csr_rom_cache *cache;
- u64 csr_addr;
-
- BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range ||
- !csr->ops->release_addr || csr->max_rom < 1);
-
- /* ROM size must be a multiple of csr->max_rom */
- romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1);
-
- csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom,
- csr->private);
- if (csr_addr == CSR1212_INVALID_ADDR_SPACE)
- return -ENOMEM;
-
- if (csr_addr < CSR1212_REGISTER_SPACE_BASE) {
- /* Invalid address returned from allocate_addr_range(). */
- csr->ops->release_addr(csr_addr, csr->private);
- return -ENOMEM;
- }
-
- cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE,
- romsize);
- if (!cache) {
- csr->ops->release_addr(csr_addr, csr->private);
- return -ENOMEM;
- }
-
- cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF,
- CSR1212_KV_ID_EXTENDED_ROM);
- if (!cache->ext_rom) {
- csr->ops->release_addr(csr_addr, csr->private);
- CSR1212_FREE(cache);
- return -ENOMEM;
- }
-
- if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) !=
- CSR1212_SUCCESS) {
- csr1212_release_keyval(cache->ext_rom);
- csr->ops->release_addr(csr_addr, csr->private);
- CSR1212_FREE(cache);
- return -ENOMEM;
- }
- cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE;
- cache->ext_rom->value.leaf.len = -1;
- cache->ext_rom->value.leaf.data = cache->data;
-
- /* Add cache to tail of cache list */
- cache->prev = csr->cache_tail;
- csr->cache_tail->next = cache;
- csr->cache_tail = cache;
- return CSR1212_SUCCESS;
-}
-
-static void csr1212_remove_cache(struct csr1212_csr *csr,
- struct csr1212_csr_rom_cache *cache)
-{
- if (csr->cache_head == cache)
- csr->cache_head = cache->next;
- if (csr->cache_tail == cache)
- csr->cache_tail = cache->prev;
-
- if (cache->prev)
- cache->prev->next = cache->next;
- if (cache->next)
- cache->next->prev = cache->prev;
-
- if (cache->ext_rom) {
- csr1212_detach_keyval_from_directory(csr->root_kv,
- cache->ext_rom);
- csr1212_release_keyval(cache->ext_rom);
- }
-
- CSR1212_FREE(cache);
-}
-
-static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir,
- struct csr1212_keyval **layout_tail)
-{
- struct csr1212_dentry *dentry;
- struct csr1212_keyval *dkv;
- struct csr1212_keyval *last_extkey_spec = NULL;
- struct csr1212_keyval *last_extkey = NULL;
- int num_entries = 0;
-
- for (dentry = dir->value.directory.dentries_head; dentry;
- dentry = dentry->next) {
- for (dkv = dentry->kv; dkv; dkv = dkv->associate) {
- /* Special Case: Extended Key Specifier_ID */
- if (dkv->key.id ==
- CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
- if (last_extkey_spec == NULL)
- last_extkey_spec = dkv;
- else if (dkv->value.immediate !=
- last_extkey_spec->value.immediate)
- last_extkey_spec = dkv;
- else
- continue;
- /* Special Case: Extended Key */
- } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
- if (last_extkey == NULL)
- last_extkey = dkv;
- else if (dkv->value.immediate !=
- last_extkey->value.immediate)
- last_extkey = dkv;
- else
- continue;
- }
-
- num_entries += 1;
-
- switch (dkv->key.type) {
- default:
- case CSR1212_KV_TYPE_IMMEDIATE:
- case CSR1212_KV_TYPE_CSR_OFFSET:
- break;
- case CSR1212_KV_TYPE_LEAF:
- case CSR1212_KV_TYPE_DIRECTORY:
- /* Remove from list */
- if (dkv->prev && (dkv->prev->next == dkv))
- dkv->prev->next = dkv->next;
- if (dkv->next && (dkv->next->prev == dkv))
- dkv->next->prev = dkv->prev;
- //if (dkv == *layout_tail)
- // *layout_tail = dkv->prev;
-
- /* Special case: Extended ROM leafs */
- if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) {
- dkv->value.leaf.len = -1;
- /* Don't add Extended ROM leafs in the
- * layout list, they are handled
- * differently. */
- break;
- }
-
- /* Add to tail of list */
- dkv->next = NULL;
- dkv->prev = *layout_tail;
- (*layout_tail)->next = dkv;
- *layout_tail = dkv;
- break;
- }
- }
- }
- return num_entries;
-}
-
-static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv)
-{
- struct csr1212_keyval *ltail = kv;
- size_t agg_size = 0;
-
- while (kv) {
- switch (kv->key.type) {
- case CSR1212_KV_TYPE_LEAF:
- /* Add 1 quadlet for crc/len field */
- agg_size += kv->value.leaf.len + 1;
- break;
-
- case CSR1212_KV_TYPE_DIRECTORY:
- kv->value.directory.len =
- csr1212_generate_layout_subdir(kv, &ltail);
- /* Add 1 quadlet for crc/len field */
- agg_size += kv->value.directory.len + 1;
- break;
- }
- kv = kv->next;
- }
- return quads_to_bytes(agg_size);
-}
-
-static struct csr1212_keyval *
-csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
- struct csr1212_keyval *start_kv, int start_pos)
-{
- struct csr1212_keyval *kv = start_kv;
- struct csr1212_keyval *okv = start_kv;
- int pos = start_pos;
- int kv_len = 0, okv_len = 0;
-
- cache->layout_head = kv;
-
- while (kv && pos < cache->size) {
- /* Special case: Extended ROM leafs */
- if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
- kv->offset = cache->offset + pos;
-
- switch (kv->key.type) {
- case CSR1212_KV_TYPE_LEAF:
- kv_len = kv->value.leaf.len;
- break;
-
- case CSR1212_KV_TYPE_DIRECTORY:
- kv_len = kv->value.directory.len;
- break;
-
- default:
- /* Should never get here */
- WARN_ON(1);
- break;
- }
-
- pos += quads_to_bytes(kv_len + 1);
-
- if (pos <= cache->size) {
- okv = kv;
- okv_len = kv_len;
- kv = kv->next;
- }
- }
-
- cache->layout_tail = okv;
- cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1);
-
- return kv;
-}
-
-#define CSR1212_KV_KEY_SHIFT 24
-#define CSR1212_KV_KEY_TYPE_SHIFT 6
-#define CSR1212_KV_KEY_ID_MASK 0x3f
-#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */
-
-static void
-csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer)
-{
- struct csr1212_dentry *dentry;
- struct csr1212_keyval *last_extkey_spec = NULL;
- struct csr1212_keyval *last_extkey = NULL;
- int index = 0;
-
- for (dentry = dir->value.directory.dentries_head;
- dentry;
- dentry = dentry->next) {
- struct csr1212_keyval *a;
-
- for (a = dentry->kv; a; a = a->associate) {
- u32 value = 0;
-
- /* Special Case: Extended Key Specifier_ID */
- if (a->key.id ==
- CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
- if (last_extkey_spec == NULL)
- last_extkey_spec = a;
- else if (a->value.immediate !=
- last_extkey_spec->value.immediate)
- last_extkey_spec = a;
- else
- continue;
-
- /* Special Case: Extended Key */
- } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
- if (last_extkey == NULL)
- last_extkey = a;
- else if (a->value.immediate !=
- last_extkey->value.immediate)
- last_extkey = a;
- else
- continue;
- }
-
- switch (a->key.type) {
- case CSR1212_KV_TYPE_IMMEDIATE:
- value = a->value.immediate;
- break;
- case CSR1212_KV_TYPE_CSR_OFFSET:
- value = a->value.csr_offset;
- break;
- case CSR1212_KV_TYPE_LEAF:
- value = a->offset;
- value -= dir->offset + quads_to_bytes(1+index);
- value = bytes_to_quads(value);
- break;
- case CSR1212_KV_TYPE_DIRECTORY:
- value = a->offset;
- value -= dir->offset + quads_to_bytes(1+index);
- value = bytes_to_quads(value);
- break;
- default:
- /* Should never get here */
- WARN_ON(1);
- break;
- }
-
- value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) <<
- CSR1212_KV_KEY_SHIFT;
- value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) <<
- (CSR1212_KV_KEY_SHIFT +
- CSR1212_KV_KEY_TYPE_SHIFT);
- data_buffer[index] = cpu_to_be32(value);
- index++;
- }
- }
-}
-
-struct csr1212_keyval_img {
- u16 length;
- u16 crc;
-
- /* Must be last */
- u32 data[0]; /* older gcc can't handle [] which is standard */
-};
-
-static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache)
-{
- struct csr1212_keyval *kv, *nkv;
- struct csr1212_keyval_img *kvi;
-
- for (kv = cache->layout_head;
- kv != cache->layout_tail->next;
- kv = nkv) {
- kvi = (struct csr1212_keyval_img *)(cache->data +
- bytes_to_quads(kv->offset - cache->offset));
- switch (kv->key.type) {
- default:
- case CSR1212_KV_TYPE_IMMEDIATE:
- case CSR1212_KV_TYPE_CSR_OFFSET:
- /* Should never get here */
- WARN_ON(1);
- break;
-
- case CSR1212_KV_TYPE_LEAF:
- /* Don't copy over Extended ROM areas, they are
- * already filled out! */
- if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
- memcpy(kvi->data, kv->value.leaf.data,
- quads_to_bytes(kv->value.leaf.len));
-
- kvi->length = cpu_to_be16(kv->value.leaf.len);
- kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len);
- break;
-
- case CSR1212_KV_TYPE_DIRECTORY:
- csr1212_generate_tree_subdir(kv, kvi->data);
-
- kvi->length = cpu_to_be16(kv->value.directory.len);
- kvi->crc = csr1212_crc16(kvi->data,
- kv->value.directory.len);
- break;
- }
-
- nkv = kv->next;
- if (kv->prev)
- kv->prev->next = NULL;
- if (kv->next)
- kv->next->prev = NULL;
- kv->prev = NULL;
- kv->next = NULL;
- }
-}
-
-/* This size is arbitrarily chosen.
- * The struct overhead is subtracted for more economic allocations. */
-#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache))
-
-int csr1212_generate_csr_image(struct csr1212_csr *csr)
-{
- struct csr1212_bus_info_block_img *bi;
- struct csr1212_csr_rom_cache *cache;
- struct csr1212_keyval *kv;
- size_t agg_size;
- int ret;
- int init_offset;
-
- BUG_ON(!csr);
-
- cache = csr->cache_head;
-
- bi = (struct csr1212_bus_info_block_img*)cache->data;
-
- bi->length = bytes_to_quads(csr->bus_info_len) - 1;
- bi->crc_length = bi->length;
- bi->crc = csr1212_crc16(bi->data, bi->crc_length);
-
- csr->root_kv->next = NULL;
- csr->root_kv->prev = NULL;
-
- agg_size = csr1212_generate_layout_order(csr->root_kv);
-
- init_offset = csr->bus_info_len;
-
- for (kv = csr->root_kv, cache = csr->cache_head;
- kv;
- cache = cache->next) {
- if (!cache) {
- /* Estimate approximate number of additional cache
- * regions needed (it assumes that the cache holding
- * the first 1K Config ROM space always exists). */
- int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE -
- (2 * sizeof(u32))) + 1;
-
- /* Add additional cache regions, extras will be
- * removed later */
- for (; est_c; est_c--) {
- ret = csr1212_append_new_cache(csr,
- CSR1212_EXTENDED_ROM_SIZE);
- if (ret != CSR1212_SUCCESS)
- return ret;
- }
- /* Need to re-layout for additional cache regions */
- agg_size = csr1212_generate_layout_order(csr->root_kv);
- kv = csr->root_kv;
- cache = csr->cache_head;
- init_offset = csr->bus_info_len;
- }
- kv = csr1212_generate_positions(cache, kv, init_offset);
- agg_size -= cache->len;
- init_offset = sizeof(u32);
- }
-
- /* Remove unused, excess cache regions */
- while (cache) {
- struct csr1212_csr_rom_cache *oc = cache;
-
- cache = cache->next;
- csr1212_remove_cache(csr, oc);
- }
-
- /* Go through the list backward so that when done, the correct CRC
- * will be calculated for the Extended ROM areas. */
- for (cache = csr->cache_tail; cache; cache = cache->prev) {
- /* Only Extended ROM caches should have this set. */
- if (cache->ext_rom) {
- int leaf_size;
-
- /* Make sure the Extended ROM leaf is a multiple of
- * max_rom in size. */
- BUG_ON(csr->max_rom < 1);
- leaf_size = (cache->len + (csr->max_rom - 1)) &
- ~(csr->max_rom - 1);
-
- /* Zero out the unused ROM region */
- memset(cache->data + bytes_to_quads(cache->len), 0x00,
- leaf_size - cache->len);
-
- /* Subtract leaf header */
- leaf_size -= sizeof(u32);
-
- /* Update the Extended ROM leaf length */
- cache->ext_rom->value.leaf.len =
- bytes_to_quads(leaf_size);
- } else {
- /* Zero out the unused ROM region */
- memset(cache->data + bytes_to_quads(cache->len), 0x00,
- cache->size - cache->len);
- }
-
- /* Copy the data into the cache buffer */
- csr1212_fill_cache(cache);
-
- if (cache != csr->cache_head) {
- /* Set the length and CRC of the extended ROM. */
- struct csr1212_keyval_img *kvi =
- (struct csr1212_keyval_img*)cache->data;
- u16 len = bytes_to_quads(cache->len) - 1;
-
- kvi->length = cpu_to_be16(len);
- kvi->crc = csr1212_crc16(kvi->data, len);
- }
- }
-
- return CSR1212_SUCCESS;
-}
-
-int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len)
-{
- struct csr1212_csr_rom_cache *cache;
-
- for (cache = csr->cache_head; cache; cache = cache->next)
- if (offset >= cache->offset &&
- (offset + len) <= (cache->offset + cache->size)) {
- memcpy(buffer, &cache->data[
- bytes_to_quads(offset - cache->offset)],
- len);
- return CSR1212_SUCCESS;
- }
-
- return -ENOENT;
-}
-
-/*
- * Apparently there are many different wrong implementations of the CRC
- * algorithm. We don't fail, we just warn... approximately once per GUID.
- */
-static void
-csr1212_check_crc(const u32 *buffer, size_t length, u16 crc, __be32 *guid)
-{
- static u64 last_bad_eui64;
- u64 eui64 = ((u64)be32_to_cpu(guid[0]) << 32) | be32_to_cpu(guid[1]);
-
- if (csr1212_crc16(buffer, length) == crc ||
- csr1212_msft_crc16(buffer, length) == crc ||
- eui64 == last_bad_eui64)
- return;
-
- printk(KERN_DEBUG "ieee1394: config ROM CRC error\n");
- last_bad_eui64 = eui64;
-}
-
-/* Parse a chunk of data as a Config ROM */
-
-static int csr1212_parse_bus_info_block(struct csr1212_csr *csr)
-{
- struct csr1212_bus_info_block_img *bi;
- struct csr1212_cache_region *cr;
- int i;
- int ret;
-
- for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) {
- ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
- &csr->cache_head->data[bytes_to_quads(i)],
- csr->private);
- if (ret != CSR1212_SUCCESS)
- return ret;
-
- /* check ROM header's info_length */
- if (i == 0 &&
- be32_to_cpu(csr->cache_head->data[0]) >> 24 !=
- bytes_to_quads(csr->bus_info_len) - 1)
- return -EINVAL;
- }
-
- bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data;
- csr->crc_len = quads_to_bytes(bi->crc_length);
-
- /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that
- * is not always the case, so read the rest of the crc area 1 quadlet at
- * a time. */
- for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) {
- ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
- &csr->cache_head->data[bytes_to_quads(i)],
- csr->private);
- if (ret != CSR1212_SUCCESS)
- return ret;
- }
-
- csr1212_check_crc(bi->data, bi->crc_length, bi->crc,
- &csr->bus_info_data[3]);
-
- cr = CSR1212_MALLOC(sizeof(*cr));
- if (!cr)
- return -ENOMEM;
-
- cr->next = NULL;
- cr->prev = NULL;
- cr->offset_start = 0;
- cr->offset_end = csr->crc_len + 4;
-
- csr->cache_head->filled_head = cr;
- csr->cache_head->filled_tail = cr;
-
- return CSR1212_SUCCESS;
-}
-
-#define CSR1212_KV_KEY(q) (be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT)
-#define CSR1212_KV_KEY_TYPE(q) (CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT)
-#define CSR1212_KV_KEY_ID(q) (CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK)
-#define CSR1212_KV_VAL_MASK 0xffffff
-#define CSR1212_KV_VAL(q) (be32_to_cpu(q) & CSR1212_KV_VAL_MASK)
-
-static int
-csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
-{
- int ret = CSR1212_SUCCESS;
- struct csr1212_keyval *k = NULL;
- u32 offset;
- bool keep_keyval = true;
-
- switch (CSR1212_KV_KEY_TYPE(ki)) {
- case CSR1212_KV_TYPE_IMMEDIATE:
- k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki),
- CSR1212_KV_VAL(ki));
- if (!k) {
- ret = -ENOMEM;
- goto out;
- }
- /* Don't keep local reference when parsing. */
- keep_keyval = false;
- break;
-
- case CSR1212_KV_TYPE_CSR_OFFSET:
- k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki),
- CSR1212_KV_VAL(ki));
- if (!k) {
- ret = -ENOMEM;
- goto out;
- }
- /* Don't keep local reference when parsing. */
- keep_keyval = false;
- break;
-
- default:
- /* Compute the offset from 0xffff f000 0000. */
- offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos;
- if (offset == kv_pos) {
- /* Uh-oh. Can't have a relative offset of 0 for Leaves
- * or Directories. The Config ROM image is most likely
- * messed up, so we'll just abort here. */
- ret = -EIO;
- goto out;
- }
-
- k = csr1212_find_keyval_offset(dir, offset);
-
- if (k)
- break; /* Found it. */
-
- if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY)
- k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki));
- else
- k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0);
-
- if (!k) {
- ret = -ENOMEM;
- goto out;
- }
- /* Don't keep local reference when parsing. */
- keep_keyval = false;
- /* Contents not read yet so it's not valid. */
- k->valid = 0;
- k->offset = offset;
-
- k->prev = dir;
- k->next = dir->next;
- dir->next->prev = k;
- dir->next = k;
- }
- ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval);
-out:
- if (ret != CSR1212_SUCCESS && k != NULL)
- free_keyval(k);
- return ret;
-}
-
-int csr1212_parse_keyval(struct csr1212_keyval *kv,
- struct csr1212_csr_rom_cache *cache)
-{
- struct csr1212_keyval_img *kvi;
- int i;
- int ret = CSR1212_SUCCESS;
- int kvi_len;
-
- kvi = (struct csr1212_keyval_img*)
- &cache->data[bytes_to_quads(kv->offset - cache->offset)];
- kvi_len = be16_to_cpu(kvi->length);
-
- /* GUID is wrong in here in case of extended ROM. We don't care. */
- csr1212_check_crc(kvi->data, kvi_len, kvi->crc, &cache->data[3]);
-
- switch (kv->key.type) {
- case CSR1212_KV_TYPE_DIRECTORY:
- for (i = 0; i < kvi_len; i++) {
- u32 ki = kvi->data[i];
-
- /* Some devices put null entries in their unit
- * directories. If we come across such an entry,
- * then skip it. */
- if (ki == 0x0)
- continue;
- ret = csr1212_parse_dir_entry(kv, ki,
- kv->offset + quads_to_bytes(i + 1));
- }
- kv->value.directory.len = kvi_len;
- break;
-
- case CSR1212_KV_TYPE_LEAF:
- if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) {
- size_t size = quads_to_bytes(kvi_len);
-
- kv->value.leaf.data = CSR1212_MALLOC(size);
- if (!kv->value.leaf.data) {
- ret = -ENOMEM;
- goto out;
- }
-
- kv->value.leaf.len = kvi_len;
- memcpy(kv->value.leaf.data, kvi->data, size);
- }
- break;
- }
-
- kv->valid = 1;
-out:
- return ret;
-}
-
-static int
-csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
-{
- struct csr1212_cache_region *cr, *ncr, *newcr = NULL;
- struct csr1212_keyval_img *kvi = NULL;
- struct csr1212_csr_rom_cache *cache;
- int cache_index;
- u64 addr;
- u32 *cache_ptr;
- u16 kv_len = 0;
-
- BUG_ON(!csr || !kv || csr->max_rom < 1);
-
- /* First find which cache the data should be in (or go in if not read
- * yet). */
- for (cache = csr->cache_head; cache; cache = cache->next)
- if (kv->offset >= cache->offset &&
- kv->offset < (cache->offset + cache->size))
- break;
-
- if (!cache) {
- u32 q, cache_size;
-
- /* Only create a new cache for Extended ROM leaves. */
- if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
- return -EINVAL;
-
- if (csr->ops->bus_read(csr,
- CSR1212_REGISTER_SPACE_BASE + kv->offset,
- &q, csr->private))
- return -EIO;
-
- kv->value.leaf.len = be32_to_cpu(q) >> 16;
-
- cache_size = (quads_to_bytes(kv->value.leaf.len + 1) +
- (csr->max_rom - 1)) & ~(csr->max_rom - 1);
-
- cache = csr1212_rom_cache_malloc(kv->offset, cache_size);
- if (!cache)
- return -ENOMEM;
-
- kv->value.leaf.data = &cache->data[1];
- csr->cache_tail->next = cache;
- cache->prev = csr->cache_tail;
- cache->next = NULL;
- csr->cache_tail = cache;
- cache->filled_head =
- CSR1212_MALLOC(sizeof(*cache->filled_head));
- if (!cache->filled_head)
- return -ENOMEM;
-
- cache->filled_head->offset_start = 0;
- cache->filled_head->offset_end = sizeof(u32);
- cache->filled_tail = cache->filled_head;
- cache->filled_head->next = NULL;
- cache->filled_head->prev = NULL;
- cache->data[0] = q;
-
- /* Don't read the entire extended ROM now. Pieces of it will
- * be read when entries inside it are read. */
- return csr1212_parse_keyval(kv, cache);
- }
-
- cache_index = kv->offset - cache->offset;
-
- /* Now seach read portions of the cache to see if it is there. */
- for (cr = cache->filled_head; cr; cr = cr->next) {
- if (cache_index < cr->offset_start) {
- newcr = CSR1212_MALLOC(sizeof(*newcr));
- if (!newcr)
- return -ENOMEM;
-
- newcr->offset_start = cache_index & ~(csr->max_rom - 1);
- newcr->offset_end = newcr->offset_start;
- newcr->next = cr;
- newcr->prev = cr->prev;
- cr->prev = newcr;
- cr = newcr;
- break;
- } else if ((cache_index >= cr->offset_start) &&
- (cache_index < cr->offset_end)) {
- kvi = (struct csr1212_keyval_img*)
- (&cache->data[bytes_to_quads(cache_index)]);
- kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);
- break;
- } else if (cache_index == cr->offset_end) {
- break;
- }
- }
-
- if (!cr) {
- cr = cache->filled_tail;
- newcr = CSR1212_MALLOC(sizeof(*newcr));
- if (!newcr)
- return -ENOMEM;
-
- newcr->offset_start = cache_index & ~(csr->max_rom - 1);
- newcr->offset_end = newcr->offset_start;
- newcr->prev = cr;
- newcr->next = cr->next;
- cr->next = newcr;
- cr = newcr;
- cache->filled_tail = newcr;
- }
-
- while(!kvi || cr->offset_end < cache_index + kv_len) {
- cache_ptr = &cache->data[bytes_to_quads(cr->offset_end &
- ~(csr->max_rom - 1))];
-
- addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset +
- cr->offset_end) & ~(csr->max_rom - 1);
-
- if (csr->ops->bus_read(csr, addr, cache_ptr, csr->private))
- return -EIO;
-
- cr->offset_end += csr->max_rom - (cr->offset_end &
- (csr->max_rom - 1));
-
- if (!kvi && (cr->offset_end > cache_index)) {
- kvi = (struct csr1212_keyval_img*)
- (&cache->data[bytes_to_quads(cache_index)]);
- kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);
- }
-
- if ((kv_len + (kv->offset - cache->offset)) > cache->size) {
- /* The Leaf or Directory claims its length extends
- * beyond the ConfigROM image region and thus beyond the
- * end of our cache region. Therefore, we abort now
- * rather than seg faulting later. */
- return -EIO;
- }
-
- ncr = cr->next;
-
- if (ncr && (cr->offset_end >= ncr->offset_start)) {
- /* consolidate region entries */
- ncr->offset_start = cr->offset_start;
-
- if (cr->prev)
- cr->prev->next = cr->next;
- ncr->prev = cr->prev;
- if (cache->filled_head == cr)
- cache->filled_head = ncr;
- CSR1212_FREE(cr);
- cr = ncr;
- }
- }
-
- return csr1212_parse_keyval(kv, cache);
-}
-
-struct csr1212_keyval *
-csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
-{
- if (!kv)
- return NULL;
- if (!kv->valid)
- if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS)
- return NULL;
- return kv;
-}
-
-int csr1212_parse_csr(struct csr1212_csr *csr)
-{
- struct csr1212_dentry *dentry;
- int ret;
-
- BUG_ON(!csr || !csr->ops || !csr->ops->bus_read);
-
- ret = csr1212_parse_bus_info_block(csr);
- if (ret != CSR1212_SUCCESS)
- return ret;
-
- /*
- * There has been a buggy firmware with bus_info_block.max_rom > 0
- * spotted which actually only supported quadlet read requests to the
- * config ROM. Therefore read everything quadlet by quadlet regardless
- * of what the bus info block says.
- */
- csr->max_rom = 4;
-
- csr->cache_head->layout_head = csr->root_kv;
- csr->cache_head->layout_tail = csr->root_kv;
-
- csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) +
- csr->bus_info_len;
-
- csr->root_kv->valid = 0;
- csr->root_kv->next = csr->root_kv;
- csr->root_kv->prev = csr->root_kv;
- ret = csr1212_read_keyval(csr, csr->root_kv);
- if (ret != CSR1212_SUCCESS)
- return ret;
-
- /* Scan through the Root directory finding all extended ROM regions
- * and make cache regions for them */
- for (dentry = csr->root_kv->value.directory.dentries_head;
- dentry; dentry = dentry->next) {
- if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM &&
- !dentry->kv->valid) {
- ret = csr1212_read_keyval(csr, dentry->kv);
- if (ret != CSR1212_SUCCESS)
- return ret;
- }
- }
-
- return CSR1212_SUCCESS;
-}
diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h
deleted file mode 100644
index a892d922dbc9..000000000000
--- a/drivers/ieee1394/csr1212.h
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * csr1212.h -- IEEE 1212 Control and Status Register support for Linux
- *
- * Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za>
- * Steve Kinneberg <kinnebergsteve@acmsystems.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __CSR1212_H__
-#define __CSR1212_H__
-
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <asm/atomic.h>
-
-#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL)
-#define CSR1212_FREE(ptr) kfree(ptr)
-
-#define CSR1212_SUCCESS (0)
-
-
-/* CSR 1212 key types */
-#define CSR1212_KV_TYPE_IMMEDIATE 0
-#define CSR1212_KV_TYPE_CSR_OFFSET 1
-#define CSR1212_KV_TYPE_LEAF 2
-#define CSR1212_KV_TYPE_DIRECTORY 3
-
-
-/* CSR 1212 key ids */
-#define CSR1212_KV_ID_DESCRIPTOR 0x01
-#define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02
-#define CSR1212_KV_ID_VENDOR 0x03
-#define CSR1212_KV_ID_HARDWARE_VERSION 0x04
-#define CSR1212_KV_ID_MODULE 0x07
-#define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C
-#define CSR1212_KV_ID_EUI_64 0x0D
-#define CSR1212_KV_ID_UNIT 0x11
-#define CSR1212_KV_ID_SPECIFIER_ID 0x12
-#define CSR1212_KV_ID_VERSION 0x13
-#define CSR1212_KV_ID_DEPENDENT_INFO 0x14
-#define CSR1212_KV_ID_UNIT_LOCATION 0x15
-#define CSR1212_KV_ID_MODEL 0x17
-#define CSR1212_KV_ID_INSTANCE 0x18
-#define CSR1212_KV_ID_KEYWORD 0x19
-#define CSR1212_KV_ID_FEATURE 0x1A
-#define CSR1212_KV_ID_EXTENDED_ROM 0x1B
-#define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C
-#define CSR1212_KV_ID_EXTENDED_KEY 0x1D
-#define CSR1212_KV_ID_EXTENDED_DATA 0x1E
-#define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F
-#define CSR1212_KV_ID_DIRECTORY_ID 0x20
-#define CSR1212_KV_ID_REVISION 0x21
-
-
-/* IEEE 1212 Address space map */
-#define CSR1212_ALL_SPACE_BASE (0x000000000000ULL)
-#define CSR1212_ALL_SPACE_SIZE (1ULL << 48)
-#define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE)
-
-#define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL)
-#define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20)))
-#define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE)
-
-#define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL)
-#define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20))
-#define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE)
-
-#define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL)
-#define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20))
-#define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE)
-
-#define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL)
-#define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512)
-#define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE)
-#define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
-
-#define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL)
-#define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512)
-#define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE)
-#define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
-
-#define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL)
-#define CSR1212_CONFIG_ROM_SPACE_SIZE (1024)
-#define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE)
-#define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
-
-#define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL)
-#define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048)
-#define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE)
-#define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
-
-#define CSR1212_INVALID_ADDR_SPACE -1
-
-
-/* Config ROM image structures */
-struct csr1212_bus_info_block_img {
- u8 length;
- u8 crc_length;
- u16 crc;
-
- /* Must be last */
- u32 data[0]; /* older gcc can't handle [] which is standard */
-};
-
-struct csr1212_leaf {
- int len;
- u32 *data;
-};
-
-struct csr1212_dentry {
- struct csr1212_dentry *next, *prev;
- struct csr1212_keyval *kv;
-};
-
-struct csr1212_directory {
- int len;
- struct csr1212_dentry *dentries_head, *dentries_tail;
-};
-
-struct csr1212_keyval {
- struct {
- u8 type;
- u8 id;
- } key;
- union {
- u32 immediate;
- u32 csr_offset;
- struct csr1212_leaf leaf;
- struct csr1212_directory directory;
- } value;
- struct csr1212_keyval *associate;
- atomic_t refcnt;
-
- /* used in generating and/or parsing CSR image */
- struct csr1212_keyval *next, *prev; /* flat list of CSR elements */
- u32 offset; /* position in CSR from 0xffff f000 0000 */
- u8 valid; /* flag indicating keyval has valid data*/
-};
-
-
-struct csr1212_cache_region {
- struct csr1212_cache_region *next, *prev;
- u32 offset_start; /* inclusive */
- u32 offset_end; /* exclusive */
-};
-
-struct csr1212_csr_rom_cache {
- struct csr1212_csr_rom_cache *next, *prev;
- struct csr1212_cache_region *filled_head, *filled_tail;
- struct csr1212_keyval *layout_head, *layout_tail;
- size_t size;
- u32 offset;
- struct csr1212_keyval *ext_rom;
- size_t len;
-
- /* Must be last */
- u32 data[0]; /* older gcc can't handle [] which is standard */
-};
-
-struct csr1212_csr {
- size_t bus_info_len; /* bus info block length in bytes */
- size_t crc_len; /* crc length in bytes */
- __be32 *bus_info_data; /* bus info data incl bus name and EUI */
-
- void *private; /* private, bus specific data */
- struct csr1212_bus_ops *ops;
-
- struct csr1212_keyval *root_kv;
-
- int max_rom; /* max bytes readable in Config ROM region */
-
- /* Items below used for image parsing and generation */
- struct csr1212_csr_rom_cache *cache_head, *cache_tail;
-};
-
-struct csr1212_bus_ops {
- /* This function is used by csr1212 to read additional information
- * from remote nodes when parsing a Config ROM (i.e., read Config ROM
- * entries located in the Units Space. Must return 0 on success
- * anything else indicates an error. */
- int (*bus_read) (struct csr1212_csr *csr, u64 addr,
- void *buffer, void *private);
-
- /* This function is used by csr1212 to allocate a region in units space
- * in the event that Config ROM entries don't all fit in the predefined
- * 1K region. The void *private parameter is private member of struct
- * csr1212_csr. */
- u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private);
-
- /* This function is used by csr1212 to release a region in units space
- * that is no longer needed. */
- void (*release_addr) (u64 addr, void *private);
-};
-
-
-/* Descriptor Leaf manipulation macros */
-#define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24
-#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff
-#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32))
-
-#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \
- (be32_to_cpu((kv)->value.leaf.data[0]) >> \
- CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)
-#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \
- (be32_to_cpu((kv)->value.leaf.data[0]) & \
- CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)
-
-
-/* Text Descriptor Leaf manipulation macros */
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32))
-
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \
- (be32_to_cpu((kv)->value.leaf.data[1]) >> \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \
- ((be32_to_cpu((kv)->value.leaf.data[1]) >> \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK)
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \
- (be32_to_cpu((kv)->value.leaf.data[1]) & \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK)
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \
- (&((kv)->value.leaf.data[2]))
-
-
-/* The following 2 function are for creating new Configuration ROM trees. The
- * first function is used for both creating local trees and parsing remote
- * trees. The second function adds pertinent information to local Configuration
- * ROM trees - namely data for the bus information block. */
-extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops,
- size_t bus_info_size,
- void *private);
-extern void csr1212_init_local_csr(struct csr1212_csr *csr,
- const u32 *bus_info_data, int max_rom);
-
-
-/* Destroy a Configuration ROM tree and release all memory taken by the tree. */
-extern void csr1212_destroy_csr(struct csr1212_csr *csr);
-
-
-/* The following set of functions are fore creating new keyvals for placement in
- * a Configuration ROM tree. Code that creates new keyvals with these functions
- * must release those keyvals with csr1212_release_keyval() when they are no
- * longer needed. */
-extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value);
-extern struct csr1212_keyval *csr1212_new_directory(u8 key);
-extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s);
-
-
-/* The following function manages association between keyvals. Typically,
- * Descriptor Leaves and Directories will be associated with another keyval and
- * it is desirable for the Descriptor keyval to be place immediately after the
- * keyval that it is associated with.
- * Take care with subsequent ROM modifications: There is no function to remove
- * previously specified associations.
- */
-extern void csr1212_associate_keyval(struct csr1212_keyval *kv,
- struct csr1212_keyval *associate);
-
-
-/* The following functions manage the association of a keyval and directories.
- * A keyval may be attached to more than one directory. */
-extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
- struct csr1212_keyval *kv);
-extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir,
- struct csr1212_keyval *kv);
-
-
-/* Creates a complete Configuration ROM image in the list of caches available
- * via csr->cache_head. */
-extern int csr1212_generate_csr_image(struct csr1212_csr *csr);
-
-
-/* This is a convience function for reading a block of data out of one of the
- * caches in the csr->cache_head list. */
-extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer,
- u32 len);
-
-
-/* The following functions are in place for parsing Configuration ROM images.
- * csr1212_parse_keyval() is used should there be a need to directly parse a
- * Configuration ROM directly. */
-extern int csr1212_parse_keyval(struct csr1212_keyval *kv,
- struct csr1212_csr_rom_cache *cache);
-extern int csr1212_parse_csr(struct csr1212_csr *csr);
-
-
-/* This function allocates a new cache which may be used for either parsing or
- * generating sub-sets of Configuration ROM images. */
-static inline struct csr1212_csr_rom_cache *
-csr1212_rom_cache_malloc(u32 offset, size_t size)
-{
- struct csr1212_csr_rom_cache *cache;
-
- cache = CSR1212_MALLOC(sizeof(*cache) + size);
- if (!cache)
- return NULL;
-
- cache->next = NULL;
- cache->prev = NULL;
- cache->filled_head = NULL;
- cache->filled_tail = NULL;
- cache->layout_head = NULL;
- cache->layout_tail = NULL;
- cache->offset = offset;
- cache->size = size;
- cache->ext_rom = NULL;
-
- return cache;
-}
-
-
-/* This function ensures that a keyval contains data when referencing a keyval
- * created by parsing a Configuration ROM. */
-extern struct csr1212_keyval *
-csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv);
-
-
-/* This function increments the reference count for a keyval should there be a
- * need for code to retain a keyval that has been parsed. */
-static inline void csr1212_keep_keyval(struct csr1212_keyval *kv)
-{
- atomic_inc(&kv->refcnt);
- smp_mb__after_atomic_inc();
-}
-
-
-/* This function decrements a keyval's reference count and will destroy the
- * keyval when there are no more users of the keyval. This should be called by
- * any code that calls csr1212_keep_keyval() or any of the keyval creation
- * routines csr1212_new_*(). */
-extern void csr1212_release_keyval(struct csr1212_keyval *kv);
-
-
-/*
- * This macro allows for looping over the keyval entries in a directory and it
- * ensures that keyvals from remote ConfigROMs are parsed properly.
- *
- * struct csr1212_csr *_csr points to the CSR associated with dir.
- * struct csr1212_keyval *_kv points to the current keyval (loop index).
- * struct csr1212_keyval *_dir points to the directory to be looped.
- * struct csr1212_dentry *_pos is used internally for indexing.
- *
- * kv will be NULL upon exit of the loop.
- */
-#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \
- for (csr1212_get_keyval((_csr), (_dir)), \
- _pos = (_dir)->value.directory.dentries_head, \
- _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\
- (_kv) && (_pos); \
- (_kv->associate == NULL) ? \
- ((_pos = _pos->next), (_kv = (_pos) ? \
- csr1212_get_keyval((_csr), _pos->kv) : \
- NULL)) : \
- (_kv = csr1212_get_keyval((_csr), _kv->associate)))
-
-#endif /* __CSR1212_H__ */
diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c
deleted file mode 100644
index d178699b194a..000000000000
--- a/drivers/ieee1394/dma.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * DMA region bookkeeping routines
- *
- * Copyright (C) 2002 Maas Digital LLC
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/vmalloc.h>
-#include <linux/scatterlist.h>
-
-#include "dma.h"
-
-/* dma_prog_region */
-
-void dma_prog_region_init(struct dma_prog_region *prog)
-{
- prog->kvirt = NULL;
- prog->dev = NULL;
- prog->n_pages = 0;
- prog->bus_addr = 0;
-}
-
-int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
- struct pci_dev *dev)
-{
- /* round up to page size */
- n_bytes = PAGE_ALIGN(n_bytes);
-
- prog->n_pages = n_bytes >> PAGE_SHIFT;
-
- prog->kvirt = pci_alloc_consistent(dev, n_bytes, &prog->bus_addr);
- if (!prog->kvirt) {
- printk(KERN_ERR
- "dma_prog_region_alloc: pci_alloc_consistent() failed\n");
- dma_prog_region_free(prog);
- return -ENOMEM;
- }
-
- prog->dev = dev;
-
- return 0;
-}
-
-void dma_prog_region_free(struct dma_prog_region *prog)
-{
- if (prog->kvirt) {
- pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT,
- prog->kvirt, prog->bus_addr);
- }
-
- prog->kvirt = NULL;
- prog->dev = NULL;
- prog->n_pages = 0;
- prog->bus_addr = 0;
-}
-
-/* dma_region */
-
-/**
- * dma_region_init - clear out all fields but do not allocate anything
- */
-void dma_region_init(struct dma_region *dma)
-{
- dma->kvirt = NULL;
- dma->dev = NULL;
- dma->n_pages = 0;
- dma->n_dma_pages = 0;
- dma->sglist = NULL;
-}
-
-/**
- * dma_region_alloc - allocate the buffer and map it to the IOMMU
- */
-int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
- struct pci_dev *dev, int direction)
-{
- unsigned int i;
-
- /* round up to page size */
- n_bytes = PAGE_ALIGN(n_bytes);
-
- dma->n_pages = n_bytes >> PAGE_SHIFT;
-
- dma->kvirt = vmalloc_32(n_bytes);
- if (!dma->kvirt) {
- printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n");
- goto err;
- }
-
- /* Clear the ram out, no junk to the user */
- memset(dma->kvirt, 0, n_bytes);
-
- /* allocate scatter/gather list */
- dma->sglist = vmalloc(dma->n_pages * sizeof(*dma->sglist));
- if (!dma->sglist) {
- printk(KERN_ERR "dma_region_alloc: vmalloc(sglist) failed\n");
- goto err;
- }
-
- sg_init_table(dma->sglist, dma->n_pages);
-
- /* fill scatter/gather list with pages */
- for (i = 0; i < dma->n_pages; i++) {
- unsigned long va =
- (unsigned long)dma->kvirt + (i << PAGE_SHIFT);
-
- sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va),
- PAGE_SIZE, 0);
- }
-
- /* map sglist to the IOMMU */
- dma->n_dma_pages =
- pci_map_sg(dev, dma->sglist, dma->n_pages, direction);
-
- if (dma->n_dma_pages == 0) {
- printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed\n");
- goto err;
- }
-
- dma->dev = dev;
- dma->direction = direction;
-
- return 0;
-
- err:
- dma_region_free(dma);
- return -ENOMEM;
-}
-
-/**
- * dma_region_free - unmap and free the buffer
- */
-void dma_region_free(struct dma_region *dma)
-{
- if (dma->n_dma_pages) {
- pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages,
- dma->direction);
- dma->n_dma_pages = 0;
- dma->dev = NULL;
- }
-
- vfree(dma->sglist);
- dma->sglist = NULL;
-
- vfree(dma->kvirt);
- dma->kvirt = NULL;
- dma->n_pages = 0;
-}
-
-/* find the scatterlist index and remaining offset corresponding to a
- given offset from the beginning of the buffer */
-static inline int dma_region_find(struct dma_region *dma, unsigned long offset,
- unsigned int start, unsigned long *rem)
-{
- int i;
- unsigned long off = offset;
-
- for (i = start; i < dma->n_dma_pages; i++) {
- if (off < sg_dma_len(&dma->sglist[i])) {
- *rem = off;
- break;
- }
-
- off -= sg_dma_len(&dma->sglist[i]);
- }
-
- BUG_ON(i >= dma->n_dma_pages);
-
- return i;
-}
-
-/**
- * dma_region_offset_to_bus - get bus address of an offset within a DMA region
- *
- * Returns the DMA bus address of the byte with the given @offset relative to
- * the beginning of the @dma.
- */
-dma_addr_t dma_region_offset_to_bus(struct dma_region * dma,
- unsigned long offset)
-{
- unsigned long rem = 0;
-
- struct scatterlist *sg =
- &dma->sglist[dma_region_find(dma, offset, 0, &rem)];
- return sg_dma_address(sg) + rem;
-}
-
-/**
- * dma_region_sync_for_cpu - sync the CPU's view of the buffer
- */
-void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
- unsigned long len)
-{
- int first, last;
- unsigned long rem = 0;
-
- if (!len)
- len = 1;
-
- first = dma_region_find(dma, offset, 0, &rem);
- last = dma_region_find(dma, rem + len - 1, first, &rem);
-
- pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1,
- dma->direction);
-}
-
-/**
- * dma_region_sync_for_device - sync the IO bus' view of the buffer
- */
-void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
- unsigned long len)
-{
- int first, last;
- unsigned long rem = 0;
-
- if (!len)
- len = 1;
-
- first = dma_region_find(dma, offset, 0, &rem);
- last = dma_region_find(dma, rem + len - 1, first, &rem);
-
- pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first],
- last - first + 1, dma->direction);
-}
-
-#ifdef CONFIG_MMU
-
-static int dma_region_pagefault(struct vm_area_struct *vma,
- struct vm_fault *vmf)
-{
- struct dma_region *dma = (struct dma_region *)vma->vm_private_data;
-
- if (!dma->kvirt)
- return VM_FAULT_SIGBUS;
-
- if (vmf->pgoff >= dma->n_pages)
- return VM_FAULT_SIGBUS;
-
- vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT));
- get_page(vmf->page);
- return 0;
-}
-
-static const struct vm_operations_struct dma_region_vm_ops = {
- .fault = dma_region_pagefault,
-};
-
-/**
- * dma_region_mmap - map the buffer into a user space process
- */
-int dma_region_mmap(struct dma_region *dma, struct file *file,
- struct vm_area_struct *vma)
-{
- unsigned long size;
-
- if (!dma->kvirt)
- return -EINVAL;
-
- /* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */
- if (vma->vm_pgoff != 0)
- return -EINVAL;
-
- /* check the length */
- size = vma->vm_end - vma->vm_start;
- if (size > (dma->n_pages << PAGE_SHIFT))
- return -EINVAL;
-
- vma->vm_ops = &dma_region_vm_ops;
- vma->vm_private_data = dma;
- vma->vm_file = file;
- vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP;
-
- return 0;
-}
-
-#else /* CONFIG_MMU */
-
-int dma_region_mmap(struct dma_region *dma, struct file *file,
- struct vm_area_struct *vma)
-{
- return -EINVAL;
-}
-
-#endif /* CONFIG_MMU */
diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h
deleted file mode 100644
index 467373cab8e5..000000000000
--- a/drivers/ieee1394/dma.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * DMA region bookkeeping routines
- *
- * Copyright (C) 2002 Maas Digital LLC
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#ifndef IEEE1394_DMA_H
-#define IEEE1394_DMA_H
-
-#include <asm/types.h>
-
-struct file;
-struct pci_dev;
-struct scatterlist;
-struct vm_area_struct;
-
-/**
- * struct dma_prog_region - small contiguous DMA buffer
- * @kvirt: kernel virtual address
- * @dev: PCI device
- * @n_pages: number of kernel pages
- * @bus_addr: base bus address
- *
- * a small, physically contiguous DMA buffer with random-access, synchronous
- * usage characteristics
- */
-struct dma_prog_region {
- unsigned char *kvirt;
- struct pci_dev *dev;
- unsigned int n_pages;
- dma_addr_t bus_addr;
-};
-
-/* clear out all fields but do not allocate any memory */
-void dma_prog_region_init(struct dma_prog_region *prog);
-int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
- struct pci_dev *dev);
-void dma_prog_region_free(struct dma_prog_region *prog);
-
-static inline dma_addr_t dma_prog_region_offset_to_bus(
- struct dma_prog_region *prog, unsigned long offset)
-{
- return prog->bus_addr + offset;
-}
-
-/**
- * struct dma_region - large non-contiguous DMA buffer
- * @virt: kernel virtual address
- * @dev: PCI device
- * @n_pages: number of kernel pages
- * @n_dma_pages: number of IOMMU pages
- * @sglist: IOMMU mapping
- * @direction: PCI_DMA_TODEVICE, etc.
- *
- * a large, non-physically-contiguous DMA buffer with streaming, asynchronous
- * usage characteristics
- */
-struct dma_region {
- unsigned char *kvirt;
- struct pci_dev *dev;
- unsigned int n_pages;
- unsigned int n_dma_pages;
- struct scatterlist *sglist;
- int direction;
-};
-
-void dma_region_init(struct dma_region *dma);
-int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
- struct pci_dev *dev, int direction);
-void dma_region_free(struct dma_region *dma);
-void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
- unsigned long len);
-void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
- unsigned long len);
-int dma_region_mmap(struct dma_region *dma, struct file *file,
- struct vm_area_struct *vma);
-dma_addr_t dma_region_offset_to_bus(struct dma_region *dma,
- unsigned long offset);
-
-/**
- * dma_region_i - macro to index into a DMA region (or dma_prog_region)
- */
-#define dma_region_i(_dma, _type, _index) \
- ( ((_type*) ((_dma)->kvirt)) + (_index) )
-
-#endif /* IEEE1394_DMA_H */
diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h
deleted file mode 100644
index 18b92cbf4a9f..000000000000
--- a/drivers/ieee1394/dv1394-private.h
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips
- * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
- * receive by Dan Dennedy <dan@dennedy.org>
- *
- * based on:
- * video1394.h - driver for OHCI 1394 boards
- * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
- * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef _DV_1394_PRIVATE_H
-#define _DV_1394_PRIVATE_H
-
-#include "ieee1394.h"
-#include "ohci1394.h"
-#include "dma.h"
-
-/* data structures private to the dv1394 driver */
-/* none of this is exposed to user-space */
-
-
-/*
- the 8-byte CIP (Common Isochronous Packet) header that precedes
- each packet of DV data.
-
- See the IEC 61883 standard.
-*/
-
-struct CIP_header { unsigned char b[8]; };
-
-static inline void fill_cip_header(struct CIP_header *cip,
- unsigned char source_node_id,
- unsigned long counter,
- enum pal_or_ntsc format,
- unsigned long timestamp)
-{
- cip->b[0] = source_node_id;
- cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */
- cip->b[2] = 0x00;
- cip->b[3] = counter;
-
- cip->b[4] = 0x80; /* const */
-
- switch(format) {
- case DV1394_PAL:
- cip->b[5] = 0x80;
- break;
- case DV1394_NTSC:
- cip->b[5] = 0x00;
- break;
- }
-
- cip->b[6] = timestamp >> 8;
- cip->b[7] = timestamp & 0xFF;
-}
-
-
-
-/*
- DMA commands used to program the OHCI's DMA engine
-
- See the Texas Instruments OHCI 1394 chipset documentation.
-*/
-
-struct output_more_immediate { __le32 q[8]; };
-struct output_more { __le32 q[4]; };
-struct output_last { __le32 q[4]; };
-struct input_more { __le32 q[4]; };
-struct input_last { __le32 q[4]; };
-
-/* outputs */
-
-static inline void fill_output_more_immediate(struct output_more_immediate *omi,
- unsigned char tag,
- unsigned char channel,
- unsigned char sync_tag,
- unsigned int payload_size)
-{
- omi->q[0] = cpu_to_le32(0x02000000 | 8); /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */
- omi->q[1] = cpu_to_le32(0);
- omi->q[2] = cpu_to_le32(0);
- omi->q[3] = cpu_to_le32(0);
-
- /* IT packet header */
- omi->q[4] = cpu_to_le32( (0x0 << 16) /* IEEE1394_SPEED_100 */
- | (tag << 14)
- | (channel << 8)
- | (TCODE_ISO_DATA << 4)
- | (sync_tag) );
-
- /* reserved field; mimic behavior of my Sony DSR-40 */
- omi->q[5] = cpu_to_le32((payload_size << 16) | (0x7F << 8) | 0xA0);
-
- omi->q[6] = cpu_to_le32(0);
- omi->q[7] = cpu_to_le32(0);
-}
-
-static inline void fill_output_more(struct output_more *om,
- unsigned int data_size,
- unsigned long data_phys_addr)
-{
- om->q[0] = cpu_to_le32(data_size);
- om->q[1] = cpu_to_le32(data_phys_addr);
- om->q[2] = cpu_to_le32(0);
- om->q[3] = cpu_to_le32(0);
-}
-
-static inline void fill_output_last(struct output_last *ol,
- int want_timestamp,
- int want_interrupt,
- unsigned int data_size,
- unsigned long data_phys_addr)
-{
- u32 temp = 0;
- temp |= 1 << 28; /* OUTPUT_LAST */
-
- if (want_timestamp) /* controller will update timestamp at DMA time */
- temp |= 1 << 27;
-
- if (want_interrupt)
- temp |= 3 << 20;
-
- temp |= 3 << 18; /* must take branch */
- temp |= data_size;
-
- ol->q[0] = cpu_to_le32(temp);
- ol->q[1] = cpu_to_le32(data_phys_addr);
- ol->q[2] = cpu_to_le32(0);
- ol->q[3] = cpu_to_le32(0);
-}
-
-/* inputs */
-
-static inline void fill_input_more(struct input_more *im,
- int want_interrupt,
- unsigned int data_size,
- unsigned long data_phys_addr)
-{
- u32 temp = 2 << 28; /* INPUT_MORE */
- temp |= 8 << 24; /* s = 1, update xferStatus and resCount */
- if (want_interrupt)
- temp |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */
- temp |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */
- /* disable wait on sync field, not used in DV :-( */
- temp |= data_size;
-
- im->q[0] = cpu_to_le32(temp);
- im->q[1] = cpu_to_le32(data_phys_addr);
- im->q[2] = cpu_to_le32(0); /* branchAddress and Z not use in packet-per-buffer mode */
- im->q[3] = cpu_to_le32(0); /* xferStatus & resCount, resCount must be initialize to data_size */
-}
-
-static inline void fill_input_last(struct input_last *il,
- int want_interrupt,
- unsigned int data_size,
- unsigned long data_phys_addr)
-{
- u32 temp = 3 << 28; /* INPUT_LAST */
- temp |= 8 << 24; /* s = 1, update xferStatus and resCount */
- if (want_interrupt)
- temp |= 3 << 20; /* enable interrupts */
- temp |= 0xC << 16; /* enable branch to address */
- /* disable wait on sync field, not used in DV :-( */
- temp |= data_size;
-
- il->q[0] = cpu_to_le32(temp);
- il->q[1] = cpu_to_le32(data_phys_addr);
- il->q[2] = cpu_to_le32(1); /* branchAddress (filled in later) and Z = 1 descriptor in next block */
- il->q[3] = cpu_to_le32(data_size); /* xferStatus & resCount, resCount must be initialize to data_size */
-}
-
-
-
-/*
- A "DMA descriptor block" consists of several contiguous DMA commands.
- struct DMA_descriptor_block encapsulates all of the commands necessary
- to send one packet of DV data.
-
- There are three different types of these blocks:
-
- 1) command to send an empty packet (CIP header only, no DV data):
-
- OUTPUT_MORE-Immediate <-- contains the iso header in-line
- OUTPUT_LAST <-- points to the CIP header
-
- 2) command to send a full packet when the DV data payload does NOT
- cross a page boundary:
-
- OUTPUT_MORE-Immediate <-- contains the iso header in-line
- OUTPUT_MORE <-- points to the CIP header
- OUTPUT_LAST <-- points to entire DV data payload
-
- 3) command to send a full packet when the DV payload DOES cross
- a page boundary:
-
- OUTPUT_MORE-Immediate <-- contains the iso header in-line
- OUTPUT_MORE <-- points to the CIP header
- OUTPUT_MORE <-- points to first part of DV data payload
- OUTPUT_LAST <-- points to second part of DV data payload
-
- This struct describes all three block types using unions.
-
- !!! It is vital that an even number of these descriptor blocks fit on one
- page of memory, since a block cannot cross a page boundary !!!
-
- */
-
-struct DMA_descriptor_block {
-
- union {
- struct {
- /* iso header, common to all output block types */
- struct output_more_immediate omi;
-
- union {
- /* empty packet */
- struct {
- struct output_last ol; /* CIP header */
- } empty;
-
- /* full packet */
- struct {
- struct output_more om; /* CIP header */
-
- union {
- /* payload does not cross page boundary */
- struct {
- struct output_last ol; /* data payload */
- } nocross;
-
- /* payload crosses page boundary */
- struct {
- struct output_more om; /* data payload */
- struct output_last ol; /* data payload */
- } cross;
- } u;
-
- } full;
- } u;
- } out;
-
- struct {
- struct input_last il;
- } in;
-
- } u;
-
- /* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0
- by padding out to 128 bytes */
- u32 __pad__[12];
-};
-
-
-/* struct frame contains all data associated with one frame in the
- ringbuffer these are allocated when the DMA context is initialized
- do_dv1394_init(). They are re-used after the card finishes
- transmitting the frame. */
-
-struct video_card; /* forward declaration */
-
-struct frame {
-
- /* points to the struct video_card that owns this frame */
- struct video_card *video;
-
- /* index of this frame in video_card->frames[] */
- unsigned int frame_num;
-
- /* FRAME_CLEAR - DMA program not set up, waiting for data
- FRAME_READY - DMA program written, ready to transmit
-
- Changes to these should be locked against the interrupt
- */
- enum {
- FRAME_CLEAR = 0,
- FRAME_READY
- } state;
-
- /* whether this frame has been DMA'ed already; used only from
- the IRQ handler to determine whether the frame can be reset */
- int done;
-
-
- /* kernel virtual pointer to the start of this frame's data in
- the user ringbuffer. Use only for CPU access; to get the DMA
- bus address you must go through the video->user_dma mapping */
- unsigned long data;
-
- /* Max # of packets per frame */
-#define MAX_PACKETS 500
-
-
- /* a PAGE_SIZE memory pool for allocating CIP headers
- !header_pool must be aligned to PAGE_SIZE! */
- struct CIP_header *header_pool;
- dma_addr_t header_pool_dma;
-
-
- /* a physically contiguous memory pool for allocating DMA
- descriptor blocks; usually around 64KB in size
- !descriptor_pool must be aligned to PAGE_SIZE! */
- struct DMA_descriptor_block *descriptor_pool;
- dma_addr_t descriptor_pool_dma;
- unsigned long descriptor_pool_size;
-
-
- /* # of packets allocated for this frame */
- unsigned int n_packets;
-
-
- /* below are several pointers (kernel virtual addresses, not
- DMA bus addresses) to parts of the DMA program. These are
- set each time the DMA program is written in
- frame_prepare(). They are used later on, e.g. from the
- interrupt handler, to check the status of the frame */
-
- /* points to status/timestamp field of first DMA packet */
- /* (we'll check it later to monitor timestamp accuracy) */
- __le32 *frame_begin_timestamp;
-
- /* the timestamp we assigned to the first packet in the frame */
- u32 assigned_timestamp;
-
- /* pointer to the first packet's CIP header (where the timestamp goes) */
- struct CIP_header *cip_syt1;
-
- /* pointer to the second packet's CIP header
- (only set if the first packet was empty) */
- struct CIP_header *cip_syt2;
-
- /* in order to figure out what caused an interrupt,
- store pointers to the status fields of the two packets
- that can cause interrupts. We'll check these from the
- interrupt handler.
- */
- __le32 *mid_frame_timestamp;
- __le32 *frame_end_timestamp;
-
- /* branch address field of final packet. This is effectively
- the "tail" in the chain of DMA descriptor blocks.
- We will fill it with the address of the first DMA descriptor
- block in the subsequent frame, once it is ready.
- */
- __le32 *frame_end_branch;
-
- /* the number of descriptors in the first descriptor block
- of the frame. Needed to start DMA */
- int first_n_descriptors;
-};
-
-
-struct packet {
- __le16 timestamp;
- u16 invalid;
- u16 iso_header;
- __le16 data_length;
- u32 cip_h1;
- u32 cip_h2;
- unsigned char data[480];
- unsigned char padding[16]; /* force struct size =512 for page alignment */
-};
-
-
-/* allocate/free a frame */
-static struct frame* frame_new(unsigned int frame_num, struct video_card *video);
-static void frame_delete(struct frame *f);
-
-/* reset f so that it can be used again */
-static void frame_reset(struct frame *f);
-
-/* struct video_card contains all data associated with one instance
- of the dv1394 driver
-*/
-enum modes {
- MODE_RECEIVE,
- MODE_TRANSMIT
-};
-
-struct video_card {
-
- /* ohci card to which this instance corresponds */
- struct ti_ohci *ohci;
-
- /* OHCI card id; the link between the VFS inode and a specific video_card
- (essentially the device minor number) */
- int id;
-
- /* entry in dv1394_cards */
- struct list_head list;
-
- /* OHCI card IT DMA context number, -1 if not in use */
- int ohci_it_ctx;
- struct ohci1394_iso_tasklet it_tasklet;
-
- /* register offsets for current IT DMA context, 0 if not in use */
- u32 ohci_IsoXmitContextControlSet;
- u32 ohci_IsoXmitContextControlClear;
- u32 ohci_IsoXmitCommandPtr;
-
- /* OHCI card IR DMA context number, -1 if not in use */
- struct ohci1394_iso_tasklet ir_tasklet;
- int ohci_ir_ctx;
-
- /* register offsets for current IR DMA context, 0 if not in use */
- u32 ohci_IsoRcvContextControlSet;
- u32 ohci_IsoRcvContextControlClear;
- u32 ohci_IsoRcvCommandPtr;
- u32 ohci_IsoRcvContextMatch;
-
-
- /* CONCURRENCY CONTROL */
-
- /* there are THREE levels of locking associated with video_card. */
-
- /*
- 1) the 'open' flag - this prevents more than one process from
- opening the device. (the driver currently assumes only one opener).
- This is a regular int, but use test_and_set_bit() (on bit zero)
- for atomicity.
- */
- unsigned long open;
-
- /*
- 2) the spinlock - this provides mutual exclusion between the interrupt
- handler and process-context operations. Generally you must take the
- spinlock under the following conditions:
- 1) DMA (and hence the interrupt handler) may be running
- AND
- 2) you need to operate on the video_card, especially active_frame
-
- It is OK to play with video_card without taking the spinlock if
- you are certain that DMA is not running. Even if DMA is running,
- it is OK to *read* active_frame with the lock, then drop it
- immediately. This is safe because the interrupt handler will never
- advance active_frame onto a frame that is not READY (and the spinlock
- must be held while marking a frame READY).
-
- spinlock is also used to protect ohci_it_ctx and ohci_ir_ctx,
- which can be accessed from both process and interrupt context
- */
- spinlock_t spinlock;
-
- /* flag to prevent spurious interrupts (which OHCI seems to
- generate a lot :) from accessing the struct */
- int dma_running;
-
- /*
- 3) the sleeping mutex 'mtx' - this is used from process context only,
- to serialize various operations on the video_card. Even though only one
- open() is allowed, we still need to prevent multiple threads of execution
- from entering calls like read, write, ioctl, etc.
-
- I honestly can't think of a good reason to use dv1394 from several threads
- at once, but we need to serialize anyway to prevent oopses =).
-
- NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock!
- */
- struct mutex mtx;
-
- /* people waiting for buffer space, please form a line here... */
- wait_queue_head_t waitq;
-
- /* support asynchronous I/O signals (SIGIO) */
- struct fasync_struct *fasync;
-
- /* the large, non-contiguous (rvmalloc()) ringbuffer for DV
- data, exposed to user-space via mmap() */
- unsigned long dv_buf_size;
- struct dma_region dv_buf;
-
- /* next byte in the ringbuffer that a write() call will fill */
- size_t write_off;
-
- struct frame *frames[DV1394_MAX_FRAMES];
-
- /* n_frames also serves as an indicator that this struct video_card is
- initialized and ready to run DMA buffers */
-
- int n_frames;
-
- /* this is the frame that is currently "owned" by the OHCI DMA controller
- (set to -1 iff DMA is not running)
-
- ! must lock against the interrupt handler when accessing it !
-
- RULES:
-
- Only the interrupt handler may change active_frame if DMA
- is running; if not, process may change it
-
- If the next frame is READY, the interrupt handler will advance
- active_frame when the current frame is finished.
-
- If the next frame is CLEAR, the interrupt handler will re-transmit
- the current frame, and the dropped_frames counter will be incremented.
-
- The interrupt handler will NEVER advance active_frame to a
- frame that is not READY.
- */
- int active_frame;
- int first_run;
-
- /* the same locking rules apply to these three fields also: */
-
- /* altered ONLY from process context. Must check first_clear_frame->state;
- if it's READY, that means the ringbuffer is full with READY frames;
- if it's CLEAR, that means one or more ringbuffer frames are CLEAR */
- unsigned int first_clear_frame;
-
- /* altered both by process and interrupt */
- unsigned int n_clear_frames;
-
- /* only altered by the interrupt */
- unsigned int dropped_frames;
-
-
-
- /* the CIP accumulator and continuity counter are properties
- of the DMA stream as a whole (not a single frame), so they
- are stored here in the video_card */
-
- unsigned long cip_accum;
- unsigned long cip_n, cip_d;
- unsigned int syt_offset;
- unsigned int continuity_counter;
-
- enum pal_or_ntsc pal_or_ntsc;
-
- /* redundant, but simplifies the code somewhat */
- unsigned int frame_size; /* in bytes */
-
- /* the isochronous channel to use, -1 if video card is inactive */
- int channel;
-
-
- /* physically contiguous packet ringbuffer for receive */
- struct dma_region packet_buf;
- unsigned long packet_buf_size;
-
- unsigned int current_packet;
- int first_frame; /* received first start frame marker? */
- enum modes mode;
-};
-
-/*
- if the video_card is not initialized, then the ONLY fields that are valid are:
- ohci
- open
- n_frames
-*/
-
-static inline int video_card_initialized(struct video_card *v)
-{
- return v->n_frames > 0;
-}
-
-static int do_dv1394_init(struct video_card *video, struct dv1394_init *init);
-static int do_dv1394_init_default(struct video_card *video);
-static void do_dv1394_shutdown(struct video_card *video, int free_user_buf);
-
-
-/* NTSC empty packet rate accurate to within 0.01%,
- calibrated against a Sony DSR-40 DVCAM deck */
-
-#define CIP_N_NTSC 68000000
-#define CIP_D_NTSC 1068000000
-
-#define CIP_N_PAL 1
-#define CIP_D_PAL 16
-
-#endif /* _DV_1394_PRIVATE_H */
-
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
deleted file mode 100644
index c5a031b79d03..000000000000
--- a/drivers/ieee1394/dv1394.c
+++ /dev/null
@@ -1,2584 +0,0 @@
-/*
- * dv1394.c - DV input/output over IEEE 1394 on OHCI chips
- * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
- * receive by Dan Dennedy <dan@dennedy.org>
- *
- * based on:
- * video1394.c - video driver for OHCI 1394 boards
- * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
- *
- * 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.
- */
-
-/*
- OVERVIEW
-
- I designed dv1394 as a "pipe" that you can use to shoot DV onto a
- FireWire bus. In transmission mode, dv1394 does the following:
-
- 1. accepts contiguous frames of DV data from user-space, via write()
- or mmap() (see dv1394.h for the complete API)
- 2. wraps IEC 61883 packets around the DV data, inserting
- empty synchronization packets as necessary
- 3. assigns accurate SYT timestamps to the outgoing packets
- 4. shoots them out using the OHCI card's IT DMA engine
-
- Thanks to Dan Dennedy, we now have a receive mode that does the following:
-
- 1. accepts raw IEC 61883 packets from the OHCI card
- 2. re-assembles the DV data payloads into contiguous frames,
- discarding empty packets
- 3. sends the DV data to user-space via read() or mmap()
-*/
-
-/*
- TODO:
-
- - tunable frame-drop behavior: either loop last frame, or halt transmission
-
- - use a scatter/gather buffer for DMA programs (f->descriptor_pool)
- so that we don't rely on allocating 64KB of contiguous kernel memory
- via pci_alloc_consistent()
-
- DONE:
- - during reception, better handling of dropped frames and continuity errors
- - during reception, prevent DMA from bypassing the irq tasklets
- - reduce irq rate during reception (1/250 packets).
- - add many more internal buffers during reception with scatter/gather dma.
- - add dbc (continuity) checking on receive, increment status.dropped_frames
- if not continuous.
- - restart IT DMA after a bus reset
- - safely obtain and release ISO Tx channels in cooperation with OHCI driver
- - map received DIF blocks to their proper location in DV frame (ensure
- recovery if dropped packet)
- - handle bus resets gracefully (OHCI card seems to take care of this itself(!))
- - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings
- - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk
- - added wmb() and mb() to places where PCI read/write ordering needs to be enforced
- - set video->id correctly
- - store video_cards in an array indexed by OHCI card ID, rather than a list
- - implement DMA context allocation to cooperate with other users of the OHCI
- - fix all XXX showstoppers
- - disable IR/IT DMA interrupts on shutdown
- - flush pci writes to the card by issuing a read
- - character device dispatching
- - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!)
- - keep all video_cards in a list (for open() via chardev), set file->private_data = video
- - dv1394_poll should indicate POLLIN when receiving buffers are available
- - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal
- - expose xmit and recv as separate devices (not exclusive)
- - expose NTSC and PAL as separate devices (can be overridden)
-
-*/
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/mutex.h>
-#include <linux/bitops.h>
-#include <asm/byteorder.h>
-#include <asm/atomic.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/delay.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/vmalloc.h>
-#include <linux/string.h>
-#include <linux/compat.h>
-#include <linux/cdev.h>
-
-#include "dv1394.h"
-#include "dv1394-private.h"
-#include "highlevel.h"
-#include "hosts.h"
-#include "ieee1394.h"
-#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
-#include "ieee1394_types.h"
-#include "nodemgr.h"
-#include "ohci1394.h"
-
-/* DEBUG LEVELS:
- 0 - no debugging messages
- 1 - some debugging messages, but none during DMA frame transmission
- 2 - lots of messages, including during DMA frame transmission
- (will cause underflows if your machine is too slow!)
-*/
-
-#define DV1394_DEBUG_LEVEL 0
-
-/* for debugging use ONLY: allow more than one open() of the device */
-/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */
-
-#if DV1394_DEBUG_LEVEL >= 2
-#define irq_printk( args... ) printk( args )
-#else
-#define irq_printk( args... ) do {} while (0)
-#endif
-
-#if DV1394_DEBUG_LEVEL >= 1
-#define debug_printk( args... ) printk( args)
-#else
-#define debug_printk( args... ) do {} while (0)
-#endif
-
-/* issue a dummy PCI read to force the preceding write
- to be posted to the PCI bus immediately */
-
-static inline void flush_pci_write(struct ti_ohci *ohci)
-{
- mb();
- reg_read(ohci, OHCI1394_IsochronousCycleTimer);
-}
-
-static void it_tasklet_func(unsigned long data);
-static void ir_tasklet_func(unsigned long data);
-
-#ifdef CONFIG_COMPAT
-static long dv1394_compat_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg);
-#endif
-
-/* GLOBAL DATA */
-
-/* list of all video_cards */
-static LIST_HEAD(dv1394_cards);
-static DEFINE_SPINLOCK(dv1394_cards_lock);
-
-/* translate from a struct file* to the corresponding struct video_card* */
-
-static inline struct video_card* file_to_video_card(struct file *file)
-{
- return file->private_data;
-}
-
-/*** FRAME METHODS *********************************************************/
-
-static void frame_reset(struct frame *f)
-{
- f->state = FRAME_CLEAR;
- f->done = 0;
- f->n_packets = 0;
- f->frame_begin_timestamp = NULL;
- f->assigned_timestamp = 0;
- f->cip_syt1 = NULL;
- f->cip_syt2 = NULL;
- f->mid_frame_timestamp = NULL;
- f->frame_end_timestamp = NULL;
- f->frame_end_branch = NULL;
-}
-
-static struct frame* frame_new(unsigned int frame_num, struct video_card *video)
-{
- struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL);
- if (!f)
- return NULL;
-
- f->video = video;
- f->frame_num = frame_num;
-
- f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma);
- if (!f->header_pool) {
- printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n");
- kfree(f);
- return NULL;
- }
-
- debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
- (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE);
-
- f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block);
- /* make it an even # of pages */
- f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE);
-
- f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev,
- f->descriptor_pool_size,
- &f->descriptor_pool_dma);
- if (!f->descriptor_pool) {
- pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
- kfree(f);
- return NULL;
- }
-
- debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
- (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size);
-
- f->data = 0;
- frame_reset(f);
-
- return f;
-}
-
-static void frame_delete(struct frame *f)
-{
- pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
- pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma);
- kfree(f);
-}
-
-
-
-
-/*
- frame_prepare() - build the DMA program for transmitting
-
- Frame_prepare() must be called OUTSIDE the video->spinlock.
- However, frame_prepare() must still be serialized, so
- it should be called WITH the video->mtx taken.
- */
-
-static void frame_prepare(struct video_card *video, unsigned int this_frame)
-{
- struct frame *f = video->frames[this_frame];
- int last_frame;
-
- struct DMA_descriptor_block *block;
- dma_addr_t block_dma;
- struct CIP_header *cip;
- dma_addr_t cip_dma;
-
- unsigned int n_descriptors, full_packets, packets_per_frame, payload_size;
-
- /* these flags denote packets that need special attention */
- int empty_packet, first_packet, last_packet, mid_packet;
-
- __le32 *branch_address, *last_branch_address = NULL;
- unsigned long data_p;
- int first_packet_empty = 0;
- u32 cycleTimer, ct_sec, ct_cyc, ct_off;
- unsigned long irq_flags;
-
- irq_printk("frame_prepare( %d ) ---------------------\n", this_frame);
-
- full_packets = 0;
-
-
-
- if (video->pal_or_ntsc == DV1394_PAL)
- packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME;
- else
- packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME;
-
- while ( full_packets < packets_per_frame ) {
- empty_packet = first_packet = last_packet = mid_packet = 0;
-
- data_p = f->data + full_packets * 480;
-
- /************************************************/
- /* allocate a descriptor block and a CIP header */
- /************************************************/
-
- /* note: these should NOT cross a page boundary (DMA restriction) */
-
- if (f->n_packets >= MAX_PACKETS) {
- printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n");
- return;
- }
-
- /* the block surely won't cross a page boundary,
- since an even number of descriptor_blocks fit on a page */
- block = &(f->descriptor_pool[f->n_packets]);
-
- /* DMA address of the block = offset of block relative
- to the kernel base address of the descriptor pool
- + DMA base address of the descriptor pool */
- block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
-
-
- /* the whole CIP pool fits on one page, so no worries about boundaries */
- if ( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool)
- > PAGE_SIZE) {
- printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n");
- return;
- }
-
- cip = &(f->header_pool[f->n_packets]);
-
- /* DMA address of the CIP header = offset of cip
- relative to kernel base address of the header pool
- + DMA base address of the header pool */
- cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma;
-
- /* is this an empty packet? */
-
- if (video->cip_accum > (video->cip_d - video->cip_n)) {
- empty_packet = 1;
- payload_size = 8;
- video->cip_accum -= (video->cip_d - video->cip_n);
- } else {
- payload_size = 488;
- video->cip_accum += video->cip_n;
- }
-
- /* there are three important packets each frame:
-
- the first packet in the frame - we ask the card to record the timestamp when
- this packet is actually sent, so we can monitor
- how accurate our timestamps are. Also, the first
- packet serves as a semaphore to let us know that
- it's OK to free the *previous* frame's DMA buffer
-
- the last packet in the frame - this packet is used to detect buffer underflows.
- if this is the last ready frame, the last DMA block
- will have a branch back to the beginning of the frame
- (so that the card will re-send the frame on underflow).
- if this branch gets taken, we know that at least one
- frame has been dropped. When the next frame is ready,
- the branch is pointed to its first packet, and the
- semaphore is disabled.
-
- a "mid" packet slightly before the end of the frame - this packet should trigger
- an interrupt so we can go and assign a timestamp to the first packet
- in the next frame. We don't use the very last packet in the frame
- for this purpose, because that would leave very little time to set
- the timestamp before DMA starts on the next frame.
- */
-
- if (f->n_packets == 0) {
- first_packet = 1;
- } else if ( full_packets == (packets_per_frame-1) ) {
- last_packet = 1;
- } else if (f->n_packets == packets_per_frame) {
- mid_packet = 1;
- }
-
-
- /********************/
- /* setup CIP header */
- /********************/
-
- /* the timestamp will be written later from the
- mid-frame interrupt handler. For now we just
- store the address of the CIP header(s) that
- need a timestamp. */
-
- /* first packet in the frame needs a timestamp */
- if (first_packet) {
- f->cip_syt1 = cip;
- if (empty_packet)
- first_packet_empty = 1;
-
- } else if (first_packet_empty && (f->n_packets == 1) ) {
- /* if the first packet was empty, the second
- packet's CIP header also needs a timestamp */
- f->cip_syt2 = cip;
- }
-
- fill_cip_header(cip,
- /* the node ID number of the OHCI card */
- reg_read(video->ohci, OHCI1394_NodeID) & 0x3F,
- video->continuity_counter,
- video->pal_or_ntsc,
- 0xFFFF /* the timestamp is filled in later */);
-
- /* advance counter, only for full packets */
- if ( ! empty_packet )
- video->continuity_counter++;
-
- /******************************/
- /* setup DMA descriptor block */
- /******************************/
-
- /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */
- fill_output_more_immediate( &(block->u.out.omi), 1, video->channel, 0, payload_size);
-
- if (empty_packet) {
- /* second descriptor - OUTPUT_LAST for CIP header */
- fill_output_last( &(block->u.out.u.empty.ol),
-
- /* want completion status on all interesting packets */
- (first_packet || mid_packet || last_packet) ? 1 : 0,
-
- /* want interrupts on all interesting packets */
- (first_packet || mid_packet || last_packet) ? 1 : 0,
-
- sizeof(struct CIP_header), /* data size */
- cip_dma);
-
- if (first_packet)
- f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]);
- else if (mid_packet)
- f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]);
- else if (last_packet) {
- f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]);
- f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]);
- }
-
- branch_address = &(block->u.out.u.empty.ol.q[2]);
- n_descriptors = 3;
- if (first_packet)
- f->first_n_descriptors = n_descriptors;
-
- } else { /* full packet */
-
- /* second descriptor - OUTPUT_MORE for CIP header */
- fill_output_more( &(block->u.out.u.full.om),
- sizeof(struct CIP_header), /* data size */
- cip_dma);
-
-
- /* third (and possibly fourth) descriptor - for DV data */
- /* the 480-byte payload can cross a page boundary; if so,
- we need to split it into two DMA descriptors */
-
- /* does the 480-byte data payload cross a page boundary? */
- if ( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) {
-
- /* page boundary crossed */
-
- fill_output_more( &(block->u.out.u.full.u.cross.om),
- /* data size - how much of data_p fits on the first page */
- PAGE_SIZE - (data_p % PAGE_SIZE),
-
- /* DMA address of data_p */
- dma_region_offset_to_bus(&video->dv_buf,
- data_p - (unsigned long) video->dv_buf.kvirt));
-
- fill_output_last( &(block->u.out.u.full.u.cross.ol),
-
- /* want completion status on all interesting packets */
- (first_packet || mid_packet || last_packet) ? 1 : 0,
-
- /* want interrupt on all interesting packets */
- (first_packet || mid_packet || last_packet) ? 1 : 0,
-
- /* data size - remaining portion of data_p */
- 480 - (PAGE_SIZE - (data_p % PAGE_SIZE)),
-
- /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */
- dma_region_offset_to_bus(&video->dv_buf,
- data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) video->dv_buf.kvirt));
-
- if (first_packet)
- f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
- else if (mid_packet)
- f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
- else if (last_packet) {
- f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
- f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]);
- }
-
- branch_address = &(block->u.out.u.full.u.cross.ol.q[2]);
-
- n_descriptors = 5;
- if (first_packet)
- f->first_n_descriptors = n_descriptors;
-
- full_packets++;
-
- } else {
- /* fits on one page */
-
- fill_output_last( &(block->u.out.u.full.u.nocross.ol),
-
- /* want completion status on all interesting packets */
- (first_packet || mid_packet || last_packet) ? 1 : 0,
-
- /* want interrupt on all interesting packets */
- (first_packet || mid_packet || last_packet) ? 1 : 0,
-
- 480, /* data size (480 bytes of DV data) */
-
-
- /* DMA address of data_p */
- dma_region_offset_to_bus(&video->dv_buf,
- data_p - (unsigned long) video->dv_buf.kvirt));
-
- if (first_packet)
- f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
- else if (mid_packet)
- f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
- else if (last_packet) {
- f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
- f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]);
- }
-
- branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]);
-
- n_descriptors = 4;
- if (first_packet)
- f->first_n_descriptors = n_descriptors;
-
- full_packets++;
- }
- }
-
- /* link this descriptor block into the DMA program by filling in
- the branch address of the previous block */
-
- /* note: we are not linked into the active DMA chain yet */
-
- if (last_branch_address) {
- *(last_branch_address) = cpu_to_le32(block_dma | n_descriptors);
- }
-
- last_branch_address = branch_address;
-
-
- f->n_packets++;
-
- }
-
- /* when we first assemble a new frame, set the final branch
- to loop back up to the top */
- *(f->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors);
-
- /* make the latest version of this frame visible to the PCI card */
- dma_region_sync_for_device(&video->dv_buf, f->data - (unsigned long) video->dv_buf.kvirt, video->frame_size);
-
- /* lock against DMA interrupt */
- spin_lock_irqsave(&video->spinlock, irq_flags);
-
- f->state = FRAME_READY;
-
- video->n_clear_frames--;
-
- last_frame = video->first_clear_frame - 1;
- if (last_frame == -1)
- last_frame = video->n_frames-1;
-
- video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
-
- irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n",
- this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame);
-
- irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n",
- (unsigned long) f->frame_begin_timestamp,
- (unsigned long) f->mid_frame_timestamp,
- (unsigned long) f->frame_end_timestamp,
- (unsigned long) f->frame_end_branch);
-
- if (video->active_frame != -1) {
-
- /* if DMA is already active, we are almost done */
- /* just link us onto the active DMA chain */
- if (video->frames[last_frame]->frame_end_branch) {
- u32 temp;
-
- /* point the previous frame's tail to this frame's head */
- *(video->frames[last_frame]->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors);
-
- /* this write MUST precede the next one, or we could silently drop frames */
- wmb();
-
- /* disable the want_status semaphore on the last packet */
- temp = le32_to_cpu(*(video->frames[last_frame]->frame_end_branch - 2));
- temp &= 0xF7CFFFFF;
- *(video->frames[last_frame]->frame_end_branch - 2) = cpu_to_le32(temp);
-
- /* flush these writes to memory ASAP */
- flush_pci_write(video->ohci);
-
- /* NOTE:
- ideally the writes should be "atomic": if
- the OHCI card reads the want_status flag in
- between them, we'll falsely report a
- dropped frame. Hopefully this window is too
- small to really matter, and the consequence
- is rather harmless. */
-
-
- irq_printk(" new frame %d linked onto DMA chain\n", this_frame);
-
- } else {
- printk(KERN_ERR "dv1394: last frame not ready???\n");
- }
-
- } else {
-
- u32 transmit_sec, transmit_cyc;
- u32 ts_cyc;
-
- /* DMA is stopped, so this is the very first frame */
- video->active_frame = this_frame;
-
- /* set CommandPtr to address and size of first descriptor block */
- reg_write(video->ohci, video->ohci_IsoXmitCommandPtr,
- video->frames[video->active_frame]->descriptor_pool_dma |
- f->first_n_descriptors);
-
- /* assign a timestamp based on the current cycle time...
- We'll tell the card to begin DMA 100 cycles from now,
- and assign a timestamp 103 cycles from now */
-
- cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer);
-
- ct_sec = cycleTimer >> 25;
- ct_cyc = (cycleTimer >> 12) & 0x1FFF;
- ct_off = cycleTimer & 0xFFF;
-
- transmit_sec = ct_sec;
- transmit_cyc = ct_cyc + 100;
-
- transmit_sec += transmit_cyc/8000;
- transmit_cyc %= 8000;
-
- ts_cyc = transmit_cyc + 3;
- ts_cyc %= 8000;
-
- f->assigned_timestamp = (ts_cyc&0xF) << 12;
-
- /* now actually write the timestamp into the appropriate CIP headers */
- if (f->cip_syt1) {
- f->cip_syt1->b[6] = f->assigned_timestamp >> 8;
- f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF;
- }
- if (f->cip_syt2) {
- f->cip_syt2->b[6] = f->assigned_timestamp >> 8;
- f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF;
- }
-
- /* --- start DMA --- */
-
- /* clear all bits in ContextControl register */
-
- reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF);
- wmb();
-
- /* the OHCI card has the ability to start ISO transmission on a
- particular cycle (start-on-cycle). This way we can ensure that
- the first DV frame will have an accurate timestamp.
-
- However, start-on-cycle only appears to work if the OHCI card
- is cycle master! Since the consequences of messing up the first
- timestamp are minimal*, just disable start-on-cycle for now.
-
- * my DV deck drops the first few frames before it "locks in;"
- so the first frame having an incorrect timestamp is inconsequential.
- */
-
-#if 0
- reg_write(video->ohci, video->ohci_IsoXmitContextControlSet,
- (1 << 31) /* enable start-on-cycle */
- | ( (transmit_sec & 0x3) << 29)
- | (transmit_cyc << 16));
- wmb();
-#endif
-
- video->dma_running = 1;
-
- /* set the 'run' bit */
- reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000);
- flush_pci_write(video->ohci);
-
- /* --- DMA should be running now --- */
-
- debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n",
- (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF,
- reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
- reg_read(video->ohci, video->ohci_IsoXmitCommandPtr));
-
- debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n",
- ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF);
-
-#if DV1394_DEBUG_LEVEL >= 2
- {
- /* check if DMA is really running */
- int i = 0;
- while (i < 20) {
- mb();
- mdelay(1);
- if (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) {
- printk("DMA ACTIVE after %d msec\n", i);
- break;
- }
- i++;
- }
-
- printk("set = %08x, cmdPtr = %08x\n",
- reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
- reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
- );
-
- if ( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
- printk("DMA did NOT go active after 20ms, event = %x\n",
- reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F);
- } else
- printk("DMA is RUNNING!\n");
- }
-#endif
-
- }
-
-
- spin_unlock_irqrestore(&video->spinlock, irq_flags);
-}
-
-
-
-/*** RECEIVE FUNCTIONS *****************************************************/
-
-/*
- frame method put_packet
-
- map and copy the packet data to its location in the frame
- based upon DIF section and sequence
-*/
-
-static void inline
-frame_put_packet (struct frame *f, struct packet *p)
-{
- int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */
- int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */
- int dif_block = p->data[2];
-
- /* sanity check */
- if (dif_sequence > 11 || dif_block > 149) return;
-
- switch (section_type) {
- case 0: /* 1 Header block */
- memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480);
- break;
-
- case 1: /* 2 Subcode blocks */
- memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480);
- break;
-
- case 2: /* 3 VAUX blocks */
- memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480);
- break;
-
- case 3: /* 9 Audio blocks interleaved with video */
- memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480);
- break;
-
- case 4: /* 135 Video blocks interleaved with audio */
- memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480);
- break;
-
- default: /* we can not handle any other data */
- break;
- }
-}
-
-
-static void start_dma_receive(struct video_card *video)
-{
- if (video->first_run == 1) {
- video->first_run = 0;
-
- /* start DMA once all of the frames are READY */
- video->n_clear_frames = 0;
- video->first_clear_frame = -1;
- video->current_packet = 0;
- video->active_frame = 0;
-
- /* reset iso recv control register */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF);
- wmb();
-
- /* clear bufferFill, set isochHeader and speed (0=100) */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000);
-
- /* match on all tags, listen on channel */
- reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel);
-
- /* address and first descriptor block + Z=1 */
- reg_write(video->ohci, video->ohci_IsoRcvCommandPtr,
- video->frames[0]->descriptor_pool_dma | 1); /* Z=1 */
- wmb();
-
- video->dma_running = 1;
-
- /* run */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000);
- flush_pci_write(video->ohci);
-
- debug_printk("dv1394: DMA started\n");
-
-#if DV1394_DEBUG_LEVEL >= 2
- {
- int i;
-
- for (i = 0; i < 1000; ++i) {
- mdelay(1);
- if (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) {
- printk("DMA ACTIVE after %d msec\n", i);
- break;
- }
- }
- if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
- printk("DEAD, event = %x\n",
- reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
- } else
- printk("RUNNING!\n");
- }
-#endif
- } else if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
- debug_printk("DEAD, event = %x\n",
- reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
-
- /* wake */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
- }
-}
-
-
-/*
- receive_packets() - build the DMA program for receiving
-*/
-
-static void receive_packets(struct video_card *video)
-{
- struct DMA_descriptor_block *block = NULL;
- dma_addr_t block_dma = 0;
- struct packet *data = NULL;
- dma_addr_t data_dma = 0;
- __le32 *last_branch_address = NULL;
- unsigned long irq_flags;
- int want_interrupt = 0;
- struct frame *f = NULL;
- int i, j;
-
- spin_lock_irqsave(&video->spinlock, irq_flags);
-
- for (j = 0; j < video->n_frames; j++) {
-
- /* connect frames */
- if (j > 0 && f != NULL && f->frame_end_branch != NULL)
- *(f->frame_end_branch) = cpu_to_le32(video->frames[j]->descriptor_pool_dma | 1); /* set Z=1 */
-
- f = video->frames[j];
-
- for (i = 0; i < MAX_PACKETS; i++) {
- /* locate a descriptor block and packet from the buffer */
- block = &(f->descriptor_pool[i]);
- block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
-
- data = ((struct packet*)video->packet_buf.kvirt) + f->frame_num * MAX_PACKETS + i;
- data_dma = dma_region_offset_to_bus( &video->packet_buf,
- ((unsigned long) data - (unsigned long) video->packet_buf.kvirt) );
-
- /* setup DMA descriptor block */
- want_interrupt = ((i % (MAX_PACKETS/2)) == 0 || i == (MAX_PACKETS-1));
- fill_input_last( &(block->u.in.il), want_interrupt, 512, data_dma);
-
- /* link descriptors */
- last_branch_address = f->frame_end_branch;
-
- if (last_branch_address != NULL)
- *(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */
-
- f->frame_end_branch = &(block->u.in.il.q[2]);
- }
-
- } /* next j */
-
- spin_unlock_irqrestore(&video->spinlock, irq_flags);
-
-}
-
-
-
-/*** MANAGEMENT FUNCTIONS **************************************************/
-
-static int do_dv1394_init(struct video_card *video, struct dv1394_init *init)
-{
- unsigned long flags, new_buf_size;
- int i;
- u64 chan_mask;
- int retval = -EINVAL;
-
- debug_printk("dv1394: initialising %d\n", video->id);
- if (init->api_version != DV1394_API_VERSION)
- return -EINVAL;
-
- /* first sanitize all the parameters */
- if ( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) )
- return -EINVAL;
-
- if ( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) )
- return -EINVAL;
-
- if ( (init->syt_offset == 0) || (init->syt_offset > 50) )
- /* default SYT offset is 3 cycles */
- init->syt_offset = 3;
-
- if (init->channel > 63)
- init->channel = 63;
-
- chan_mask = (u64)1 << init->channel;
-
- /* calculate what size DMA buffer is needed */
- if (init->format == DV1394_NTSC)
- new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames;
- else
- new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames;
-
- /* round up to PAGE_SIZE */
- if (new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE);
-
- /* don't allow the user to allocate the DMA buffer more than once */
- if (video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) {
- printk("dv1394: re-sizing the DMA buffer is not allowed\n");
- return -EINVAL;
- }
-
- /* shutdown the card if it's currently active */
- /* (the card should not be reset if the parameters are screwy) */
-
- do_dv1394_shutdown(video, 0);
-
- /* try to claim the ISO channel */
- spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
- if (video->ohci->ISO_channel_usage & chan_mask) {
- spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
- retval = -EBUSY;
- goto err;
- }
- video->ohci->ISO_channel_usage |= chan_mask;
- spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
-
- video->channel = init->channel;
-
- /* initialize misc. fields of video */
- video->n_frames = init->n_frames;
- video->pal_or_ntsc = init->format;
-
- video->cip_accum = 0;
- video->continuity_counter = 0;
-
- video->active_frame = -1;
- video->first_clear_frame = 0;
- video->n_clear_frames = video->n_frames;
- video->dropped_frames = 0;
-
- video->write_off = 0;
-
- video->first_run = 1;
- video->current_packet = -1;
- video->first_frame = 0;
-
- if (video->pal_or_ntsc == DV1394_NTSC) {
- video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC;
- video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC;
- video->frame_size = DV1394_NTSC_FRAME_SIZE;
- } else {
- video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL;
- video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL;
- video->frame_size = DV1394_PAL_FRAME_SIZE;
- }
-
- video->syt_offset = init->syt_offset;
-
- /* find and claim DMA contexts on the OHCI card */
-
- if (video->ohci_it_ctx == -1) {
- ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT,
- it_tasklet_func, (unsigned long) video);
-
- if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) {
- printk(KERN_ERR "dv1394: could not find an available IT DMA context\n");
- retval = -EBUSY;
- goto err;
- }
-
- video->ohci_it_ctx = video->it_tasklet.context;
- debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
- }
-
- if (video->ohci_ir_ctx == -1) {
- ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE,
- ir_tasklet_func, (unsigned long) video);
-
- if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) {
- printk(KERN_ERR "dv1394: could not find an available IR DMA context\n");
- retval = -EBUSY;
- goto err;
- }
- video->ohci_ir_ctx = video->ir_tasklet.context;
- debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
- }
-
- /* allocate struct frames */
- for (i = 0; i < init->n_frames; i++) {
- video->frames[i] = frame_new(i, video);
-
- if (!video->frames[i]) {
- printk(KERN_ERR "dv1394: Cannot allocate frame structs\n");
- retval = -ENOMEM;
- goto err;
- }
- }
-
- if (!video->dv_buf.kvirt) {
- /* allocate the ringbuffer */
- retval = dma_region_alloc(&video->dv_buf, new_buf_size, video->ohci->dev, PCI_DMA_TODEVICE);
- if (retval)
- goto err;
-
- video->dv_buf_size = new_buf_size;
-
- debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n",
- video->n_frames, video->dv_buf.n_pages,
- video->dv_buf.n_dma_pages, video->dv_buf_size);
- }
-
- /* set up the frame->data pointers */
- for (i = 0; i < video->n_frames; i++)
- video->frames[i]->data = (unsigned long) video->dv_buf.kvirt + i * video->frame_size;
-
- if (!video->packet_buf.kvirt) {
- /* allocate packet buffer */
- video->packet_buf_size = sizeof(struct packet) * video->n_frames * MAX_PACKETS;
- if (video->packet_buf_size % PAGE_SIZE)
- video->packet_buf_size += PAGE_SIZE - (video->packet_buf_size % PAGE_SIZE);
-
- retval = dma_region_alloc(&video->packet_buf, video->packet_buf_size,
- video->ohci->dev, PCI_DMA_FROMDEVICE);
- if (retval)
- goto err;
-
- debug_printk("dv1394: Allocated %d packets in buffer, total %u pages (%u DMA pages), %lu bytes\n",
- video->n_frames*MAX_PACKETS, video->packet_buf.n_pages,
- video->packet_buf.n_dma_pages, video->packet_buf_size);
- }
-
- /* set up register offsets for IT context */
- /* IT DMA context registers are spaced 16 bytes apart */
- video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx;
- video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx;
- video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx;
-
- /* enable interrupts for IT context */
- reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx));
- debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx);
-
- /* set up register offsets for IR context */
- /* IR DMA context registers are spaced 32 bytes apart */
- video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx;
- video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx;
- video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx;
- video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx;
-
- /* enable interrupts for IR context */
- reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) );
- debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx);
-
- return 0;
-
-err:
- do_dv1394_shutdown(video, 1);
- return retval;
-}
-
-/* if the user doesn't bother to call ioctl(INIT) before starting
- mmap() or read()/write(), just give him some default values */
-
-static int do_dv1394_init_default(struct video_card *video)
-{
- struct dv1394_init init;
-
- init.api_version = DV1394_API_VERSION;
- init.n_frames = DV1394_MAX_FRAMES / 4;
- init.channel = video->channel;
- init.format = video->pal_or_ntsc;
- init.cip_n = video->cip_n;
- init.cip_d = video->cip_d;
- init.syt_offset = video->syt_offset;
-
- return do_dv1394_init(video, &init);
-}
-
-/* do NOT call from interrupt context */
-static void stop_dma(struct video_card *video)
-{
- unsigned long flags;
- int i;
-
- /* no interrupts */
- spin_lock_irqsave(&video->spinlock, flags);
-
- video->dma_running = 0;
-
- if ( (video->ohci_it_ctx == -1) && (video->ohci_ir_ctx == -1) )
- goto out;
-
- /* stop DMA if in progress */
- if ( (video->active_frame != -1) ||
- (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
- (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
-
- /* clear the .run bits */
- reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15));
- reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15));
- flush_pci_write(video->ohci);
-
- video->active_frame = -1;
- video->first_run = 1;
-
- /* wait until DMA really stops */
- i = 0;
- while (i < 1000) {
-
- /* wait 0.1 millisecond */
- udelay(100);
-
- if ( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
- (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
- /* still active */
- debug_printk("dv1394: stop_dma: DMA not stopped yet\n" );
- mb();
- } else {
- debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10);
- break;
- }
-
- i++;
- }
-
- if (i == 1000) {
- printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10);
- }
- }
- else
- debug_printk("dv1394: stop_dma: already stopped.\n");
-
-out:
- spin_unlock_irqrestore(&video->spinlock, flags);
-}
-
-
-
-static void do_dv1394_shutdown(struct video_card *video, int free_dv_buf)
-{
- int i;
-
- debug_printk("dv1394: shutdown...\n");
-
- /* stop DMA if in progress */
- stop_dma(video);
-
- /* release the DMA contexts */
- if (video->ohci_it_ctx != -1) {
- video->ohci_IsoXmitContextControlSet = 0;
- video->ohci_IsoXmitContextControlClear = 0;
- video->ohci_IsoXmitCommandPtr = 0;
-
- /* disable interrupts for IT context */
- reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx));
-
- /* remove tasklet */
- ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet);
- debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx);
- video->ohci_it_ctx = -1;
- }
-
- if (video->ohci_ir_ctx != -1) {
- video->ohci_IsoRcvContextControlSet = 0;
- video->ohci_IsoRcvContextControlClear = 0;
- video->ohci_IsoRcvCommandPtr = 0;
- video->ohci_IsoRcvContextMatch = 0;
-
- /* disable interrupts for IR context */
- reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx));
-
- /* remove tasklet */
- ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet);
- debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx);
- video->ohci_ir_ctx = -1;
- }
-
- /* release the ISO channel */
- if (video->channel != -1) {
- u64 chan_mask;
- unsigned long flags;
-
- chan_mask = (u64)1 << video->channel;
-
- spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
- video->ohci->ISO_channel_usage &= ~(chan_mask);
- spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
-
- video->channel = -1;
- }
-
- /* free the frame structs */
- for (i = 0; i < DV1394_MAX_FRAMES; i++) {
- if (video->frames[i])
- frame_delete(video->frames[i]);
- video->frames[i] = NULL;
- }
-
- video->n_frames = 0;
-
- /* we can't free the DMA buffer unless it is guaranteed that
- no more user-space mappings exist */
-
- if (free_dv_buf) {
- dma_region_free(&video->dv_buf);
- video->dv_buf_size = 0;
- }
-
- /* free packet buffer */
- dma_region_free(&video->packet_buf);
- video->packet_buf_size = 0;
-
- debug_printk("dv1394: shutdown OK\n");
-}
-
-/*
- **********************************
- *** MMAP() THEORY OF OPERATION ***
- **********************************
-
- The ringbuffer cannot be re-allocated or freed while
- a user program maintains a mapping of it. (note that a mapping
- can persist even after the device fd is closed!)
-
- So, only let the user process allocate the DMA buffer once.
- To resize or deallocate it, you must close the device file
- and open it again.
-
- Previously Dan M. hacked out a scheme that allowed the DMA
- buffer to change by forcefully unmapping it from the user's
- address space. It was prone to error because it's very hard to
- track all the places the buffer could have been mapped (we
- would have had to walk the vma list of every process in the
- system to be sure we found all the mappings!). Instead, we
- force the user to choose one buffer size and stick with
- it. This small sacrifice is worth the huge reduction in
- error-prone code in dv1394.
-*/
-
-static int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct video_card *video = file_to_video_card(file);
- int retval = -EINVAL;
-
- /*
- * We cannot use the blocking variant mutex_lock here because .mmap
- * is called with mmap_sem held, while .ioctl, .read, .write acquire
- * video->mtx and subsequently call copy_to/from_user which will
- * grab mmap_sem in case of a page fault.
- */
- if (!mutex_trylock(&video->mtx))
- return -EAGAIN;
-
- if ( ! video_card_initialized(video) ) {
- retval = do_dv1394_init_default(video);
- if (retval)
- goto out;
- }
-
- retval = dma_region_mmap(&video->dv_buf, file, vma);
-out:
- mutex_unlock(&video->mtx);
- return retval;
-}
-
-/*** DEVICE FILE INTERFACE *************************************************/
-
-/* no need to serialize, multiple threads OK */
-static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct video_card *video = file_to_video_card(file);
- unsigned int mask = 0;
- unsigned long flags;
-
- poll_wait(file, &video->waitq, wait);
-
- spin_lock_irqsave(&video->spinlock, flags);
- if ( video->n_frames == 0 ) {
-
- } else if ( video->active_frame == -1 ) {
- /* nothing going on */
- mask |= POLLOUT;
- } else {
- /* any clear/ready buffers? */
- if (video->n_clear_frames >0)
- mask |= POLLOUT | POLLIN;
- }
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- return mask;
-}
-
-static int dv1394_fasync(int fd, struct file *file, int on)
-{
- /* I just copied this code verbatim from Alan Cox's mouse driver example
- (Documentation/DocBook/) */
-
- struct video_card *video = file_to_video_card(file);
-
- return fasync_helper(fd, file, on, &video->fasync);
-}
-
-static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
-{
- struct video_card *video = file_to_video_card(file);
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- size_t cnt;
- unsigned long flags;
- int target_frame;
-
- /* serialize this to prevent multi-threaded mayhem */
- if (file->f_flags & O_NONBLOCK) {
- if (!mutex_trylock(&video->mtx))
- return -EAGAIN;
- } else {
- if (mutex_lock_interruptible(&video->mtx))
- return -ERESTARTSYS;
- }
-
- if ( !video_card_initialized(video) ) {
- ret = do_dv1394_init_default(video);
- if (ret) {
- mutex_unlock(&video->mtx);
- return ret;
- }
- }
-
- ret = 0;
- add_wait_queue(&video->waitq, &wait);
-
- while (count > 0) {
-
- /* must set TASK_INTERRUPTIBLE *before* checking for free
- buffers; otherwise we could miss a wakeup if the interrupt
- fires between the check and the schedule() */
-
- set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_irqsave(&video->spinlock, flags);
-
- target_frame = video->first_clear_frame;
-
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- if (video->frames[target_frame]->state == FRAME_CLEAR) {
-
- /* how much room is left in the target frame buffer */
- cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
-
- } else {
- /* buffer is already used */
- cnt = 0;
- }
-
- if (cnt > count)
- cnt = count;
-
- if (cnt <= 0) {
- /* no room left, gotta wait */
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- break;
- }
-
- schedule();
-
- continue; /* start over from 'while(count > 0)...' */
- }
-
- if (copy_from_user(video->dv_buf.kvirt + video->write_off, buffer, cnt)) {
- if (!ret)
- ret = -EFAULT;
- break;
- }
-
- video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
-
- count -= cnt;
- buffer += cnt;
- ret += cnt;
-
- if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames))
- frame_prepare(video, target_frame);
- }
-
- remove_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_RUNNING);
- mutex_unlock(&video->mtx);
- return ret;
-}
-
-
-static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
-{
- struct video_card *video = file_to_video_card(file);
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- size_t cnt;
- unsigned long flags;
- int target_frame;
-
- /* serialize this to prevent multi-threaded mayhem */
- if (file->f_flags & O_NONBLOCK) {
- if (!mutex_trylock(&video->mtx))
- return -EAGAIN;
- } else {
- if (mutex_lock_interruptible(&video->mtx))
- return -ERESTARTSYS;
- }
-
- if ( !video_card_initialized(video) ) {
- ret = do_dv1394_init_default(video);
- if (ret) {
- mutex_unlock(&video->mtx);
- return ret;
- }
- video->continuity_counter = -1;
-
- receive_packets(video);
-
- start_dma_receive(video);
- }
-
- ret = 0;
- add_wait_queue(&video->waitq, &wait);
-
- while (count > 0) {
-
- /* must set TASK_INTERRUPTIBLE *before* checking for free
- buffers; otherwise we could miss a wakeup if the interrupt
- fires between the check and the schedule() */
-
- set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_irqsave(&video->spinlock, flags);
-
- target_frame = video->first_clear_frame;
-
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- if (target_frame >= 0 &&
- video->n_clear_frames > 0 &&
- video->frames[target_frame]->state == FRAME_CLEAR) {
-
- /* how much room is left in the target frame buffer */
- cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
-
- } else {
- /* buffer is already used */
- cnt = 0;
- }
-
- if (cnt > count)
- cnt = count;
-
- if (cnt <= 0) {
- /* no room left, gotta wait */
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- break;
- }
-
- schedule();
-
- continue; /* start over from 'while(count > 0)...' */
- }
-
- if (copy_to_user(buffer, video->dv_buf.kvirt + video->write_off, cnt)) {
- if (!ret)
- ret = -EFAULT;
- break;
- }
-
- video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
-
- count -= cnt;
- buffer += cnt;
- ret += cnt;
-
- if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) {
- spin_lock_irqsave(&video->spinlock, flags);
- video->n_clear_frames--;
- video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
- spin_unlock_irqrestore(&video->spinlock, flags);
- }
- }
-
- remove_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_RUNNING);
- mutex_unlock(&video->mtx);
- return ret;
-}
-
-
-/*** DEVICE IOCTL INTERFACE ************************************************/
-
-static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct video_card *video = file_to_video_card(file);
- unsigned long flags;
- int ret = -EINVAL;
- void __user *argp = (void __user *)arg;
-
- DECLARE_WAITQUEUE(wait, current);
-
- /* serialize this to prevent multi-threaded mayhem */
- if (file->f_flags & O_NONBLOCK) {
- if (!mutex_trylock(&video->mtx))
- return -EAGAIN;
- } else {
- if (mutex_lock_interruptible(&video->mtx))
- return -ERESTARTSYS;
- }
-
- switch(cmd)
- {
- case DV1394_IOC_SUBMIT_FRAMES: {
- unsigned int n_submit;
-
- if ( !video_card_initialized(video) ) {
- ret = do_dv1394_init_default(video);
- if (ret)
- goto out;
- }
-
- n_submit = (unsigned int) arg;
-
- if (n_submit > video->n_frames) {
- ret = -EINVAL;
- goto out;
- }
-
- while (n_submit > 0) {
-
- add_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_irqsave(&video->spinlock, flags);
-
- /* wait until video->first_clear_frame is really CLEAR */
- while (video->frames[video->first_clear_frame]->state != FRAME_CLEAR) {
-
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- if (signal_pending(current)) {
- remove_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_RUNNING);
- ret = -EINTR;
- goto out;
- }
-
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_irqsave(&video->spinlock, flags);
- }
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- remove_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_RUNNING);
-
- frame_prepare(video, video->first_clear_frame);
-
- n_submit--;
- }
-
- ret = 0;
- break;
- }
-
- case DV1394_IOC_WAIT_FRAMES: {
- unsigned int n_wait;
-
- if ( !video_card_initialized(video) ) {
- ret = -EINVAL;
- goto out;
- }
-
- n_wait = (unsigned int) arg;
-
- /* since we re-run the last frame on underflow, we will
- never actually have n_frames clear frames; at most only
- n_frames - 1 */
-
- if (n_wait > (video->n_frames-1) ) {
- ret = -EINVAL;
- goto out;
- }
-
- add_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_irqsave(&video->spinlock, flags);
-
- while (video->n_clear_frames < n_wait) {
-
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- if (signal_pending(current)) {
- remove_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_RUNNING);
- ret = -EINTR;
- goto out;
- }
-
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_irqsave(&video->spinlock, flags);
- }
-
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- remove_wait_queue(&video->waitq, &wait);
- set_current_state(TASK_RUNNING);
- ret = 0;
- break;
- }
-
- case DV1394_IOC_RECEIVE_FRAMES: {
- unsigned int n_recv;
-
- if ( !video_card_initialized(video) ) {
- ret = -EINVAL;
- goto out;
- }
-
- n_recv = (unsigned int) arg;
-
- /* at least one frame must be active */
- if (n_recv > (video->n_frames-1) ) {
- ret = -EINVAL;
- goto out;
- }
-
- spin_lock_irqsave(&video->spinlock, flags);
-
- /* release the clear frames */
- video->n_clear_frames -= n_recv;
-
- /* advance the clear frame cursor */
- video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames;
-
- /* reset dropped_frames */
- video->dropped_frames = 0;
-
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- ret = 0;
- break;
- }
-
- case DV1394_IOC_START_RECEIVE: {
- if ( !video_card_initialized(video) ) {
- ret = do_dv1394_init_default(video);
- if (ret)
- goto out;
- }
-
- video->continuity_counter = -1;
-
- receive_packets(video);
-
- start_dma_receive(video);
-
- ret = 0;
- break;
- }
-
- case DV1394_IOC_INIT: {
- struct dv1394_init init;
- if (!argp) {
- ret = do_dv1394_init_default(video);
- } else {
- if (copy_from_user(&init, argp, sizeof(init))) {
- ret = -EFAULT;
- goto out;
- }
- ret = do_dv1394_init(video, &init);
- }
- break;
- }
-
- case DV1394_IOC_SHUTDOWN:
- do_dv1394_shutdown(video, 0);
- ret = 0;
- break;
-
-
- case DV1394_IOC_GET_STATUS: {
- struct dv1394_status status;
-
- if ( !video_card_initialized(video) ) {
- ret = -EINVAL;
- goto out;
- }
-
- status.init.api_version = DV1394_API_VERSION;
- status.init.channel = video->channel;
- status.init.n_frames = video->n_frames;
- status.init.format = video->pal_or_ntsc;
- status.init.cip_n = video->cip_n;
- status.init.cip_d = video->cip_d;
- status.init.syt_offset = video->syt_offset;
-
- status.first_clear_frame = video->first_clear_frame;
-
- /* the rest of the fields need to be locked against the interrupt */
- spin_lock_irqsave(&video->spinlock, flags);
-
- status.active_frame = video->active_frame;
- status.n_clear_frames = video->n_clear_frames;
-
- status.dropped_frames = video->dropped_frames;
-
- /* reset dropped_frames */
- video->dropped_frames = 0;
-
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- if (copy_to_user(argp, &status, sizeof(status))) {
- ret = -EFAULT;
- goto out;
- }
-
- ret = 0;
- break;
- }
-
- default:
- break;
- }
-
- out:
- mutex_unlock(&video->mtx);
- return ret;
-}
-
-/*** DEVICE FILE INTERFACE CONTINUED ***************************************/
-
-static int dv1394_open(struct inode *inode, struct file *file)
-{
- struct video_card *video = NULL;
-
- if (file->private_data) {
- video = file->private_data;
-
- } else {
- /* look up the card by ID */
- unsigned long flags;
- int idx = ieee1394_file_to_instance(file);
-
- spin_lock_irqsave(&dv1394_cards_lock, flags);
- if (!list_empty(&dv1394_cards)) {
- struct video_card *p;
- list_for_each_entry(p, &dv1394_cards, list) {
- if ((p->id) == idx) {
- video = p;
- break;
- }
- }
- }
- spin_unlock_irqrestore(&dv1394_cards_lock, flags);
-
- if (!video) {
- debug_printk("dv1394: OHCI card %d not found", idx);
- return -ENODEV;
- }
-
- file->private_data = (void*) video;
- }
-
-#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN
-
- if ( test_and_set_bit(0, &video->open) ) {
- /* video is already open by someone else */
- return -EBUSY;
- }
-
-#endif
-
- printk(KERN_INFO "%s: NOTE, the dv1394 interface is unsupported "
- "and will not be available in the new firewire driver stack. "
- "Try libraw1394 based programs instead.\n", current->comm);
-
- return nonseekable_open(inode, file);
-}
-
-
-static int dv1394_release(struct inode *inode, struct file *file)
-{
- struct video_card *video = file_to_video_card(file);
-
- /* OK to free the DMA buffer, no more mappings can exist */
- do_dv1394_shutdown(video, 1);
-
- /* give someone else a turn */
- clear_bit(0, &video->open);
-
- return 0;
-}
-
-
-/*** DEVICE DRIVER HANDLERS ************************************************/
-
-static void it_tasklet_func(unsigned long data)
-{
- int wake = 0;
- struct video_card *video = (struct video_card*) data;
-
- spin_lock(&video->spinlock);
-
- if (!video->dma_running)
- goto out;
-
- irq_printk("ContextControl = %08x, CommandPtr = %08x\n",
- reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
- reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
- );
-
-
- if ( (video->ohci_it_ctx != -1) &&
- (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
-
- struct frame *f;
- unsigned int frame, i;
-
-
- if (video->active_frame == -1)
- frame = 0;
- else
- frame = video->active_frame;
-
- /* check all the DMA-able frames */
- for (i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) {
-
- irq_printk("IRQ checking frame %d...", frame);
- f = video->frames[frame];
- if (f->state != FRAME_READY) {
- irq_printk("clear, skipping\n");
- /* we don't own this frame */
- continue;
- }
-
- irq_printk("DMA\n");
-
- /* check the frame begin semaphore to see if we can free the previous frame */
- if ( *(f->frame_begin_timestamp) ) {
- int prev_frame;
- struct frame *prev_f;
-
-
-
- /* don't reset, need this later *(f->frame_begin_timestamp) = 0; */
- irq_printk(" BEGIN\n");
-
- prev_frame = frame - 1;
- if (prev_frame == -1)
- prev_frame += video->n_frames;
- prev_f = video->frames[prev_frame];
-
- /* make sure we can actually garbage collect
- this frame */
- if ( (prev_f->state == FRAME_READY) &&
- prev_f->done && (!f->done) )
- {
- frame_reset(prev_f);
- video->n_clear_frames++;
- wake = 1;
- video->active_frame = frame;
-
- irq_printk(" BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame);
- } else {
- irq_printk(" BEGIN - can't free yet\n");
- }
-
- f->done = 1;
- }
-
-
- /* see if we need to set the timestamp for the next frame */
- if ( *(f->mid_frame_timestamp) ) {
- struct frame *next_frame;
- u32 begin_ts, ts_cyc, ts_off;
-
- *(f->mid_frame_timestamp) = 0;
-
- begin_ts = le32_to_cpu(*(f->frame_begin_timestamp));
-
- irq_printk(" MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n",
- begin_ts & 0x1FFF, begin_ts & 0xF,
- f->assigned_timestamp >> 12, f->assigned_timestamp & 0xFFF);
-
- /* prepare next frame and assign timestamp */
- next_frame = video->frames[ (frame+1) % video->n_frames ];
-
- if (next_frame->state == FRAME_READY) {
- irq_printk(" MIDDLE - next frame is ready, good\n");
- } else {
- debug_printk("dv1394: Underflow! At least one frame has been dropped.\n");
- next_frame = f;
- }
-
- /* set the timestamp to the timestamp of the last frame sent,
- plus the length of the last frame sent, plus the syt latency */
- ts_cyc = begin_ts & 0xF;
- /* advance one frame, plus syt latency (typically 2-3) */
- ts_cyc += f->n_packets + video->syt_offset ;
-
- ts_off = 0;
-
- ts_cyc += ts_off/3072;
- ts_off %= 3072;
-
- next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off;
- if (next_frame->cip_syt1) {
- next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8;
- next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF;
- }
- if (next_frame->cip_syt2) {
- next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8;
- next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF;
- }
-
- }
-
- /* see if the frame looped */
- if ( *(f->frame_end_timestamp) ) {
-
- *(f->frame_end_timestamp) = 0;
-
- debug_printk(" END - the frame looped at least once\n");
-
- video->dropped_frames++;
- }
-
- } /* for (each frame) */
- }
-
- if (wake) {
- kill_fasync(&video->fasync, SIGIO, POLL_OUT);
-
- /* wake readers/writers/ioctl'ers */
- wake_up_interruptible(&video->waitq);
- }
-
-out:
- spin_unlock(&video->spinlock);
-}
-
-static void ir_tasklet_func(unsigned long data)
-{
- int wake = 0;
- struct video_card *video = (struct video_card*) data;
-
- spin_lock(&video->spinlock);
-
- if (!video->dma_running)
- goto out;
-
- if ( (video->ohci_ir_ctx != -1) &&
- (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) {
-
- int sof=0; /* start-of-frame flag */
- struct frame *f;
- u16 packet_length;
- int i, dbc=0;
- struct DMA_descriptor_block *block = NULL;
- u16 xferstatus;
-
- int next_i, prev_i;
- struct DMA_descriptor_block *next = NULL;
- dma_addr_t next_dma = 0;
- struct DMA_descriptor_block *prev = NULL;
-
- /* loop over all descriptors in all frames */
- for (i = 0; i < video->n_frames*MAX_PACKETS; i++) {
- struct packet *p = dma_region_i(&video->packet_buf, struct packet, video->current_packet);
-
- /* make sure we are seeing the latest changes to p */
- dma_region_sync_for_cpu(&video->packet_buf,
- (unsigned long) p - (unsigned long) video->packet_buf.kvirt,
- sizeof(struct packet));
-
- packet_length = le16_to_cpu(p->data_length);
-
- /* get the descriptor based on packet_buffer cursor */
- f = video->frames[video->current_packet / MAX_PACKETS];
- block = &(f->descriptor_pool[video->current_packet % MAX_PACKETS]);
- xferstatus = le32_to_cpu(block->u.in.il.q[3]) >> 16;
- xferstatus &= 0x1F;
- irq_printk("ir_tasklet_func: xferStatus/resCount [%d] = 0x%08x\n", i, le32_to_cpu(block->u.in.il.q[3]) );
-
- /* get the current frame */
- f = video->frames[video->active_frame];
-
- /* exclude empty packet */
- if (packet_length > 8 && xferstatus == 0x11) {
- /* check for start of frame */
- /* DRD> Changed to check section type ([0]>>5==0)
- and dif sequence ([1]>>4==0) */
- sof = ( (p->data[0] >> 5) == 0 && (p->data[1] >> 4) == 0);
-
- dbc = (int) (p->cip_h1 >> 24);
- if ( video->continuity_counter != -1 && dbc > ((video->continuity_counter + 1) % 256) )
- {
- printk(KERN_WARNING "dv1394: discontinuity detected, dropping all frames\n" );
- video->dropped_frames += video->n_clear_frames + 1;
- video->first_frame = 0;
- video->n_clear_frames = 0;
- video->first_clear_frame = -1;
- }
- video->continuity_counter = dbc;
-
- if (!video->first_frame) {
- if (sof) {
- video->first_frame = 1;
- }
-
- } else if (sof) {
- /* close current frame */
- frame_reset(f); /* f->state = STATE_CLEAR */
- video->n_clear_frames++;
- if (video->n_clear_frames > video->n_frames) {
- video->dropped_frames++;
- printk(KERN_WARNING "dv1394: dropped a frame during reception\n" );
- video->n_clear_frames = video->n_frames-1;
- video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
- }
- if (video->first_clear_frame == -1)
- video->first_clear_frame = video->active_frame;
-
- /* get the next frame */
- video->active_frame = (video->active_frame + 1) % video->n_frames;
- f = video->frames[video->active_frame];
- irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n",
- video->active_frame, video->n_clear_frames, video->first_clear_frame);
- }
- if (video->first_frame) {
- if (sof) {
- /* open next frame */
- f->state = FRAME_READY;
- }
-
- /* copy to buffer */
- if (f->n_packets > (video->frame_size / 480)) {
- printk(KERN_ERR "frame buffer overflow during receive\n");
- }
-
- frame_put_packet(f, p);
-
- } /* first_frame */
- }
-
- /* stop, end of ready packets */
- else if (xferstatus == 0) {
- break;
- }
-
- /* reset xferStatus & resCount */
- block->u.in.il.q[3] = cpu_to_le32(512);
-
- /* terminate dma chain at this (next) packet */
- next_i = video->current_packet;
- f = video->frames[next_i / MAX_PACKETS];
- next = &(f->descriptor_pool[next_i % MAX_PACKETS]);
- next_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
- next->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */
- next->u.in.il.q[2] = cpu_to_le32(0); /* disable branch */
-
- /* link previous to next */
- prev_i = (next_i == 0) ? (MAX_PACKETS * video->n_frames - 1) : (next_i - 1);
- f = video->frames[prev_i / MAX_PACKETS];
- prev = &(f->descriptor_pool[prev_i % MAX_PACKETS]);
- if (prev_i % (MAX_PACKETS/2)) {
- prev->u.in.il.q[0] &= ~cpu_to_le32(3 << 20); /* no interrupt */
- } else {
- prev->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */
- }
- prev->u.in.il.q[2] = cpu_to_le32(next_dma | 1); /* set Z=1 */
- wmb();
-
- /* wake up DMA in case it fell asleep */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
-
- /* advance packet_buffer cursor */
- video->current_packet = (video->current_packet + 1) % (MAX_PACKETS * video->n_frames);
-
- } /* for all packets */
-
- wake = 1; /* why the hell not? */
-
- } /* receive interrupt */
-
- if (wake) {
- kill_fasync(&video->fasync, SIGIO, POLL_IN);
-
- /* wake readers/writers/ioctl'ers */
- wake_up_interruptible(&video->waitq);
- }
-
-out:
- spin_unlock(&video->spinlock);
-}
-
-static struct cdev dv1394_cdev;
-static const struct file_operations dv1394_fops=
-{
- .owner = THIS_MODULE,
- .poll = dv1394_poll,
- .unlocked_ioctl = dv1394_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = dv1394_compat_ioctl,
-#endif
- .mmap = dv1394_mmap,
- .open = dv1394_open,
- .write = dv1394_write,
- .read = dv1394_read,
- .release = dv1394_release,
- .fasync = dv1394_fasync,
- .llseek = no_llseek,
-};
-
-
-/*** HOTPLUG STUFF **********************************************************/
-/*
- * Export information about protocols/devices supported by this driver.
- */
-#ifdef MODULE
-static const struct ieee1394_device_id dv1394_id_table[] = {
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = AVC_SW_VERSION_ENTRY & 0xffffff
- },
- { }
-};
-
-MODULE_DEVICE_TABLE(ieee1394, dv1394_id_table);
-#endif /* MODULE */
-
-static struct hpsb_protocol_driver dv1394_driver = {
- .name = "dv1394",
-};
-
-
-/*** IEEE1394 HPSB CALLBACKS ***********************************************/
-
-static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes mode)
-{
- struct video_card *video;
- unsigned long flags;
- int i;
-
- video = kzalloc(sizeof(*video), GFP_KERNEL);
- if (!video) {
- printk(KERN_ERR "dv1394: cannot allocate video_card\n");
- return -1;
- }
-
- video->ohci = ohci;
- /* lower 2 bits of id indicate which of four "plugs"
- per host */
- video->id = ohci->host->id << 2;
- if (format == DV1394_NTSC)
- video->id |= mode;
- else
- video->id |= 2 + mode;
-
- video->ohci_it_ctx = -1;
- video->ohci_ir_ctx = -1;
-
- video->ohci_IsoXmitContextControlSet = 0;
- video->ohci_IsoXmitContextControlClear = 0;
- video->ohci_IsoXmitCommandPtr = 0;
-
- video->ohci_IsoRcvContextControlSet = 0;
- video->ohci_IsoRcvContextControlClear = 0;
- video->ohci_IsoRcvCommandPtr = 0;
- video->ohci_IsoRcvContextMatch = 0;
-
- video->n_frames = 0; /* flag that video is not initialized */
- video->channel = 63; /* default to broadcast channel */
- video->active_frame = -1;
-
- /* initialize the following */
- video->pal_or_ntsc = format;
- video->cip_n = 0; /* 0 = use builtin default */
- video->cip_d = 0;
- video->syt_offset = 0;
- video->mode = mode;
-
- for (i = 0; i < DV1394_MAX_FRAMES; i++)
- video->frames[i] = NULL;
-
- dma_region_init(&video->dv_buf);
- video->dv_buf_size = 0;
- dma_region_init(&video->packet_buf);
- video->packet_buf_size = 0;
-
- clear_bit(0, &video->open);
- spin_lock_init(&video->spinlock);
- video->dma_running = 0;
- mutex_init(&video->mtx);
- init_waitqueue_head(&video->waitq);
- video->fasync = NULL;
-
- spin_lock_irqsave(&dv1394_cards_lock, flags);
- INIT_LIST_HEAD(&video->list);
- list_add_tail(&video->list, &dv1394_cards);
- spin_unlock_irqrestore(&dv1394_cards_lock, flags);
-
- debug_printk("dv1394: dv1394_init() OK on ID %d\n", video->id);
- return 0;
-}
-
-static void dv1394_remove_host(struct hpsb_host *host)
-{
- struct video_card *video, *tmp_video;
- unsigned long flags;
- int found_ohci_card = 0;
-
- do {
- video = NULL;
- spin_lock_irqsave(&dv1394_cards_lock, flags);
- list_for_each_entry(tmp_video, &dv1394_cards, list) {
- if ((tmp_video->id >> 2) == host->id) {
- list_del(&tmp_video->list);
- video = tmp_video;
- found_ohci_card = 1;
- break;
- }
- }
- spin_unlock_irqrestore(&dv1394_cards_lock, flags);
-
- if (video) {
- do_dv1394_shutdown(video, 1);
- kfree(video);
- }
- } while (video);
-
- if (found_ohci_card)
- device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR,
- IEEE1394_MINOR_BLOCK_DV1394 * 16 + (host->id << 2)));
-}
-
-static void dv1394_add_host(struct hpsb_host *host)
-{
- struct ti_ohci *ohci;
- int id = host->id;
-
- /* We only work with the OHCI-1394 driver */
- if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
- return;
-
- ohci = (struct ti_ohci *)host->hostdata;
-
- device_create(hpsb_protocol_class, NULL,
- MKDEV(IEEE1394_MAJOR,
- IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)),
- NULL, "dv1394-%d", id);
-
- dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE);
- dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT);
- dv1394_init(ohci, DV1394_PAL, MODE_RECEIVE);
- dv1394_init(ohci, DV1394_PAL, MODE_TRANSMIT);
-}
-
-
-/* Bus reset handler. In the event of a bus reset, we may need to
- re-start the DMA contexts - otherwise the user program would
- end up waiting forever.
-*/
-
-static void dv1394_host_reset(struct hpsb_host *host)
-{
- struct video_card *video = NULL, *tmp_vid;
- unsigned long flags;
-
- /* We only work with the OHCI-1394 driver */
- if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
- return;
-
- /* find the corresponding video_cards */
- spin_lock_irqsave(&dv1394_cards_lock, flags);
- list_for_each_entry(tmp_vid, &dv1394_cards, list) {
- if ((tmp_vid->id >> 2) == host->id) {
- video = tmp_vid;
- break;
- }
- }
- spin_unlock_irqrestore(&dv1394_cards_lock, flags);
-
- if (!video)
- return;
-
-
- spin_lock_irqsave(&video->spinlock, flags);
-
- if (!video->dma_running)
- goto out;
-
- /* check IT context */
- if (video->ohci_it_ctx != -1) {
- u32 ctx;
-
- ctx = reg_read(video->ohci, video->ohci_IsoXmitContextControlSet);
-
- /* if (RUN but not ACTIVE) */
- if ( (ctx & (1<<15)) &&
- !(ctx & (1<<10)) ) {
-
- debug_printk("dv1394: IT context stopped due to bus reset; waking it up\n");
-
- /* to be safe, assume a frame has been dropped. User-space programs
- should handle this condition like an underflow. */
- video->dropped_frames++;
-
- /* for some reason you must clear, then re-set the RUN bit to restart DMA */
-
- /* clear RUN */
- reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15));
- flush_pci_write(video->ohci);
-
- /* set RUN */
- reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 15));
- flush_pci_write(video->ohci);
-
- /* set the WAKE bit (just in case; this isn't strictly necessary) */
- reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 12));
- flush_pci_write(video->ohci);
-
- irq_printk("dv1394: AFTER IT restart ctx 0x%08x ptr 0x%08x\n",
- reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
- reg_read(video->ohci, video->ohci_IsoXmitCommandPtr));
- }
- }
-
- /* check IR context */
- if (video->ohci_ir_ctx != -1) {
- u32 ctx;
-
- ctx = reg_read(video->ohci, video->ohci_IsoRcvContextControlSet);
-
- /* if (RUN but not ACTIVE) */
- if ( (ctx & (1<<15)) &&
- !(ctx & (1<<10)) ) {
-
- debug_printk("dv1394: IR context stopped due to bus reset; waking it up\n");
-
- /* to be safe, assume a frame has been dropped. User-space programs
- should handle this condition like an overflow. */
- video->dropped_frames++;
-
- /* for some reason you must clear, then re-set the RUN bit to restart DMA */
- /* XXX this doesn't work for me, I can't get IR DMA to restart :[ */
-
- /* clear RUN */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15));
- flush_pci_write(video->ohci);
-
- /* set RUN */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 15));
- flush_pci_write(video->ohci);
-
- /* set the WAKE bit (just in case; this isn't strictly necessary) */
- reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
- flush_pci_write(video->ohci);
-
- irq_printk("dv1394: AFTER IR restart ctx 0x%08x ptr 0x%08x\n",
- reg_read(video->ohci, video->ohci_IsoRcvContextControlSet),
- reg_read(video->ohci, video->ohci_IsoRcvCommandPtr));
- }
- }
-
-out:
- spin_unlock_irqrestore(&video->spinlock, flags);
-
- /* wake readers/writers/ioctl'ers */
- wake_up_interruptible(&video->waitq);
-}
-
-static struct hpsb_highlevel dv1394_highlevel = {
- .name = "dv1394",
- .add_host = dv1394_add_host,
- .remove_host = dv1394_remove_host,
- .host_reset = dv1394_host_reset,
-};
-
-#ifdef CONFIG_COMPAT
-
-#define DV1394_IOC32_INIT _IOW('#', 0x06, struct dv1394_init32)
-#define DV1394_IOC32_GET_STATUS _IOR('#', 0x0c, struct dv1394_status32)
-
-struct dv1394_init32 {
- u32 api_version;
- u32 channel;
- u32 n_frames;
- u32 format;
- u32 cip_n;
- u32 cip_d;
- u32 syt_offset;
-};
-
-struct dv1394_status32 {
- struct dv1394_init32 init;
- s32 active_frame;
- u32 first_clear_frame;
- u32 n_clear_frames;
- u32 dropped_frames;
-};
-
-/* RED-PEN: this should use compat_alloc_userspace instead */
-
-static int handle_dv1394_init(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct dv1394_init32 dv32;
- struct dv1394_init dv;
- mm_segment_t old_fs;
- int ret;
-
- if (file->f_op->unlocked_ioctl != dv1394_ioctl)
- return -EFAULT;
-
- if (copy_from_user(&dv32, (void __user *)arg, sizeof(dv32)))
- return -EFAULT;
-
- dv.api_version = dv32.api_version;
- dv.channel = dv32.channel;
- dv.n_frames = dv32.n_frames;
- dv.format = dv32.format;
- dv.cip_n = (unsigned long)dv32.cip_n;
- dv.cip_d = (unsigned long)dv32.cip_d;
- dv.syt_offset = dv32.syt_offset;
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = dv1394_ioctl(file, DV1394_IOC_INIT, (unsigned long)&dv);
- set_fs(old_fs);
-
- return ret;
-}
-
-static int handle_dv1394_get_status(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct dv1394_status32 dv32;
- struct dv1394_status dv;
- mm_segment_t old_fs;
- int ret;
-
- if (file->f_op->unlocked_ioctl != dv1394_ioctl)
- return -EFAULT;
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = dv1394_ioctl(file, DV1394_IOC_GET_STATUS, (unsigned long)&dv);
- set_fs(old_fs);
-
- if (!ret) {
- dv32.init.api_version = dv.init.api_version;
- dv32.init.channel = dv.init.channel;
- dv32.init.n_frames = dv.init.n_frames;
- dv32.init.format = dv.init.format;
- dv32.init.cip_n = (u32)dv.init.cip_n;
- dv32.init.cip_d = (u32)dv.init.cip_d;
- dv32.init.syt_offset = dv.init.syt_offset;
- dv32.active_frame = dv.active_frame;
- dv32.first_clear_frame = dv.first_clear_frame;
- dv32.n_clear_frames = dv.n_clear_frames;
- dv32.dropped_frames = dv.dropped_frames;
-
- if (copy_to_user((struct dv1394_status32 __user *)arg, &dv32, sizeof(dv32)))
- ret = -EFAULT;
- }
-
- return ret;
-}
-
-
-
-static long dv1394_compat_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- switch (cmd) {
- case DV1394_IOC_SHUTDOWN:
- case DV1394_IOC_SUBMIT_FRAMES:
- case DV1394_IOC_WAIT_FRAMES:
- case DV1394_IOC_RECEIVE_FRAMES:
- case DV1394_IOC_START_RECEIVE:
- return dv1394_ioctl(file, cmd, arg);
-
- case DV1394_IOC32_INIT:
- return handle_dv1394_init(file, cmd, arg);
- case DV1394_IOC32_GET_STATUS:
- return handle_dv1394_get_status(file, cmd, arg);
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-#endif /* CONFIG_COMPAT */
-
-
-/*** KERNEL MODULE HANDLERS ************************************************/
-
-MODULE_AUTHOR("Dan Maas <dmaas@dcine.com>, Dan Dennedy <dan@dennedy.org>");
-MODULE_DESCRIPTION("driver for DV input/output on OHCI board");
-MODULE_SUPPORTED_DEVICE("dv1394");
-MODULE_LICENSE("GPL");
-
-static void __exit dv1394_exit_module(void)
-{
- hpsb_unregister_protocol(&dv1394_driver);
- hpsb_unregister_highlevel(&dv1394_highlevel);
- cdev_del(&dv1394_cdev);
-}
-
-static int __init dv1394_init_module(void)
-{
- int ret;
-
- cdev_init(&dv1394_cdev, &dv1394_fops);
- dv1394_cdev.owner = THIS_MODULE;
- ret = cdev_add(&dv1394_cdev, IEEE1394_DV1394_DEV, 16);
- if (ret) {
- printk(KERN_ERR "dv1394: unable to register character device\n");
- return ret;
- }
-
- hpsb_register_highlevel(&dv1394_highlevel);
-
- ret = hpsb_register_protocol(&dv1394_driver);
- if (ret) {
- printk(KERN_ERR "dv1394: failed to register protocol\n");
- hpsb_unregister_highlevel(&dv1394_highlevel);
- cdev_del(&dv1394_cdev);
- return ret;
- }
-
- return 0;
-}
-
-module_init(dv1394_init_module);
-module_exit(dv1394_exit_module);
diff --git a/drivers/ieee1394/dv1394.h b/drivers/ieee1394/dv1394.h
deleted file mode 100644
index 5807f5289810..000000000000
--- a/drivers/ieee1394/dv1394.h
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * dv1394.h - DV input/output over IEEE 1394 on OHCI chips
- * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
- * receive by Dan Dennedy <dan@dennedy.org>
- *
- * based on:
- * video1394.h - driver for OHCI 1394 boards
- * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
- * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef _DV_1394_H
-#define _DV_1394_H
-
-/* This is the public user-space interface. Try not to break it. */
-
-#define DV1394_API_VERSION 0x20011127
-
-/* ********************
- ** **
- ** DV1394 API **
- ** **
- ********************
-
- There are two methods of operating the DV1394 DV output device.
-
- 1)
-
- The simplest is an interface based on write(): simply write
- full DV frames of data to the device, and they will be transmitted
- as quickly as possible. The FD may be set for non-blocking I/O,
- in which case you can use select() or poll() to wait for output
- buffer space.
-
- To set the DV output parameters (e.g. whether you want NTSC or PAL
- video), use the DV1394_INIT ioctl, passing in the parameters you
- want in a struct dv1394_init.
-
- Example 1:
- To play a raw .DV file: cat foo.DV > /dev/dv1394
- (cat will use write() internally)
-
- Example 2:
- static struct dv1394_init init = {
- 0x63, (broadcast channel)
- 4, (four-frame ringbuffer)
- DV1394_NTSC, (send NTSC video)
- 0, 0 (default empty packet rate)
- }
-
- ioctl(fd, DV1394_INIT, &init);
-
- while (1) {
- read( <a raw DV file>, buf, DV1394_NTSC_FRAME_SIZE );
- write( <the dv1394 FD>, buf, DV1394_NTSC_FRAME_SIZE );
- }
-
- 2)
-
- For more control over buffering, and to avoid unnecessary copies
- of the DV data, you can use the more sophisticated the mmap() interface.
- First, call the DV1394_INIT ioctl to specify your parameters,
- including the number of frames in the ringbuffer. Then, calling mmap()
- on the dv1394 device will give you direct access to the ringbuffer
- from which the DV card reads your frame data.
-
- The ringbuffer is simply one large, contiguous region of memory
- containing two or more frames of packed DV data. Each frame of DV data
- is 120000 bytes (NTSC) or 144000 bytes (PAL).
-
- Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES
- ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl
- or select()/poll() to wait until the frames are transmitted. Next, you'll
- need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer
- frames are clear (ready to be filled with new DV data). Finally, use
- DV1394_SUBMIT_FRAMES again to send the new data to the DV output.
-
-
- Example: here is what a four-frame ringbuffer might look like
- during DV transmission:
-
-
- frame 0 frame 1 frame 2 frame 3
-
- *--------------------------------------*
- | CLEAR | DV data | DV data | CLEAR |
- *--------------------------------------*
- <ACTIVE>
-
- transmission goes in this direction --->>>
-
-
- The DV hardware is currently transmitting the data in frame 1.
- Once frame 1 is finished, it will automatically transmit frame 2.
- (if frame 2 finishes before frame 3 is submitted, the device
- will continue to transmit frame 2, and will increase the dropped_frames
- counter each time it repeats the transmission).
-
-
- If you called DV1394_GET_STATUS at this instant, you would
- receive the following values:
-
- n_frames = 4
- active_frame = 1
- first_clear_frame = 3
- n_clear_frames = 2
-
- At this point, you should write new DV data into frame 3 and optionally
- frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that
- it may transmit the new frames.
-
- ERROR HANDLING
-
- An error (buffer underflow/overflow or a break in the DV stream due
- to a 1394 bus reset) can be detected by checking the dropped_frames
- field of struct dv1394_status (obtained through the
- DV1394_GET_STATUS ioctl).
-
- The best way to recover from such an error is to re-initialize
- dv1394, either by using the DV1394_INIT ioctl call, or closing the
- file descriptor and opening it again. (note that you must unmap all
- ringbuffer mappings when closing the file descriptor, or else
- dv1394 will still be considered 'in use').
-
- MAIN LOOP
-
- For maximum efficiency and robustness against bus errors, you are
- advised to model the main loop of your application after the
- following pseudo-code example:
-
- (checks of system call return values omitted for brevity; always
- check return values in your code!)
-
- while ( frames left ) {
-
- struct pollfd *pfd = ...;
-
- pfd->fd = dv1394_fd;
- pfd->revents = 0;
- pfd->events = POLLOUT | POLLIN; (OUT for transmit, IN for receive)
-
- (add other sources of I/O here)
-
- poll(pfd, 1, -1); (or select(); add a timeout if you want)
-
- if (pfd->revents) {
- struct dv1394_status status;
-
- ioctl(dv1394_fd, DV1394_GET_STATUS, &status);
-
- if (status.dropped_frames > 0) {
- reset_dv1394();
- } else {
- for (int i = 0; i < status.n_clear_frames; i++) {
- copy_DV_frame();
- }
- }
- }
- }
-
- where copy_DV_frame() reads or writes on the dv1394 file descriptor
- (read/write mode) or copies data to/from the mmap ringbuffer and
- then calls ioctl(DV1394_SUBMIT_FRAMES) to notify dv1394 that new
- frames are availble (mmap mode).
-
- reset_dv1394() is called in the event of a buffer
- underflow/overflow or a halt in the DV stream (e.g. due to a 1394
- bus reset). To guarantee recovery from the error, this function
- should close the dv1394 file descriptor (and munmap() all
- ringbuffer mappings, if you are using them), then re-open the
- dv1394 device (and re-map the ringbuffer).
-
-*/
-
-
-/* maximum number of frames in the ringbuffer */
-#define DV1394_MAX_FRAMES 32
-
-/* number of *full* isochronous packets per DV frame */
-#define DV1394_NTSC_PACKETS_PER_FRAME 250
-#define DV1394_PAL_PACKETS_PER_FRAME 300
-
-/* size of one frame's worth of DV data, in bytes */
-#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME)
-#define DV1394_PAL_FRAME_SIZE (480 * DV1394_PAL_PACKETS_PER_FRAME)
-
-
-/* ioctl() commands */
-#include "ieee1394-ioctl.h"
-
-
-enum pal_or_ntsc {
- DV1394_NTSC = 0,
- DV1394_PAL
-};
-
-
-
-
-/* this is the argument to DV1394_INIT */
-struct dv1394_init {
- /* DV1394_API_VERSION */
- unsigned int api_version;
-
- /* isochronous transmission channel to use */
- unsigned int channel;
-
- /* number of frames in the ringbuffer. Must be at least 2
- and at most DV1394_MAX_FRAMES. */
- unsigned int n_frames;
-
- /* send/receive PAL or NTSC video format */
- enum pal_or_ntsc format;
-
- /* the following are used only for transmission */
-
- /* set these to zero unless you want a
- non-default empty packet rate (see below) */
- unsigned long cip_n;
- unsigned long cip_d;
-
- /* set this to zero unless you want a
- non-default SYT cycle offset (default = 3 cycles) */
- unsigned int syt_offset;
-};
-
-/* NOTE: you may only allocate the DV frame ringbuffer once each time
- you open the dv1394 device. DV1394_INIT will fail if you call it a
- second time with different 'n_frames' or 'format' arguments (which
- would imply a different size for the ringbuffer). If you need a
- different buffer size, simply close and re-open the device, then
- initialize it with your new settings. */
-
-/* Q: What are cip_n and cip_d? */
-
-/*
- A: DV video streams do not utilize 100% of the potential bandwidth offered
- by IEEE 1394 (FireWire). To achieve the correct rate of data transmission,
- DV devices must periodically insert empty packets into the 1394 data stream.
- Typically there is one empty packet per 14-16 data-carrying packets.
-
- Some DV devices will accept a wide range of empty packet rates, while others
- require a precise rate. If the dv1394 driver produces empty packets at
- a rate that your device does not accept, you may see ugly patterns on the
- DV output, or even no output at all.
-
- The default empty packet insertion rate seems to work for many people; if
- your DV output is stable, you can simply ignore this discussion. However,
- we have exposed the empty packet rate as a parameter to support devices that
- do not work with the default rate.
-
- The decision to insert an empty packet is made with a numerator/denominator
- algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D.
- You can alter the empty packet rate by passing non-zero values for cip_n
- and cip_d to the INIT ioctl.
-
- */
-
-
-
-struct dv1394_status {
- /* this embedded init struct returns the current dv1394
- parameters in use */
- struct dv1394_init init;
-
- /* the ringbuffer frame that is currently being
- displayed. (-1 if the device is not transmitting anything) */
- int active_frame;
-
- /* index of the first buffer (ahead of active_frame) that
- is ready to be filled with data */
- unsigned int first_clear_frame;
-
- /* how many buffers, including first_clear_buffer, are
- ready to be filled with data */
- unsigned int n_clear_frames;
-
- /* how many times the DV stream has underflowed, overflowed,
- or otherwise encountered an error, since the previous call
- to DV1394_GET_STATUS */
- unsigned int dropped_frames;
-
- /* N.B. The dropped_frames counter is only a lower bound on the actual
- number of dropped frames, with the special case that if dropped_frames
- is zero, then it is guaranteed that NO frames have been dropped
- since the last call to DV1394_GET_STATUS.
- */
-};
-
-
-#endif /* _DV_1394_H */
diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c
deleted file mode 100644
index 63403822330e..000000000000
--- a/drivers/ieee1394/eth1394.c
+++ /dev/null
@@ -1,1720 +0,0 @@
-/*
- * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem
- *
- * Copyright (C) 2001-2003 Ben Collins <bcollins@debian.org>
- * 2000 Bonin Franck <boninf@free.fr>
- * 2003 Steve Kinneberg <kinnebergsteve@acmsystems.com>
- *
- * Mainly based on work by Emanuel Pirker and Andreas E. Bombe
- *
- * 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.
- */
-
-/*
- * This driver intends to support RFC 2734, which describes a method for
- * transporting IPv4 datagrams over IEEE-1394 serial busses.
- *
- * TODO:
- * RFC 2734 related:
- * - Add MCAP. Limited Multicast exists only to 224.0.0.1 and 224.0.0.2.
- *
- * Non-RFC 2734 related:
- * - Handle fragmented skb's coming from the networking layer.
- * - Move generic GASP reception to core 1394 code
- * - Convert kmalloc/kfree for link fragments to use kmem_cache_* instead
- * - Stability improvements
- * - Performance enhancements
- * - Consider garbage collecting old partial datagrams after X amount of time
- */
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/workqueue.h>
-
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/ip.h>
-#include <linux/in.h>
-#include <linux/tcp.h>
-#include <linux/skbuff.h>
-#include <linux/bitops.h>
-#include <asm/uaccess.h>
-#include <asm/delay.h>
-#include <asm/unaligned.h>
-#include <net/arp.h>
-
-#include "config_roms.h"
-#include "csr1212.h"
-#include "eth1394.h"
-#include "highlevel.h"
-#include "ieee1394.h"
-#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
-#include "ieee1394_transactions.h"
-#include "ieee1394_types.h"
-#include "iso.h"
-#include "nodemgr.h"
-
-#define ETH1394_PRINT_G(level, fmt, args...) \
- printk(level "%s: " fmt, driver_name, ## args)
-
-#define ETH1394_PRINT(level, dev_name, fmt, args...) \
- printk(level "%s: %s: " fmt, driver_name, dev_name, ## args)
-
-struct fragment_info {
- struct list_head list;
- int offset;
- int len;
-};
-
-struct partial_datagram {
- struct list_head list;
- u16 dgl;
- u16 dg_size;
- __be16 ether_type;
- struct sk_buff *skb;
- char *pbuf;
- struct list_head frag_info;
-};
-
-struct pdg_list {
- struct list_head list; /* partial datagram list per node */
- unsigned int sz; /* partial datagram list size per node */
- spinlock_t lock; /* partial datagram lock */
-};
-
-struct eth1394_host_info {
- struct hpsb_host *host;
- struct net_device *dev;
-};
-
-struct eth1394_node_ref {
- struct unit_directory *ud;
- struct list_head list;
-};
-
-struct eth1394_node_info {
- u16 maxpayload; /* max payload */
- u8 sspd; /* max speed */
- u64 fifo; /* FIFO address */
- struct pdg_list pdg; /* partial RX datagram lists */
- int dgl; /* outgoing datagram label */
-};
-
-static const char driver_name[] = "eth1394";
-
-static struct kmem_cache *packet_task_cache;
-
-static struct hpsb_highlevel eth1394_highlevel;
-
-/* Use common.lf to determine header len */
-static const int hdr_type_len[] = {
- sizeof(struct eth1394_uf_hdr),
- sizeof(struct eth1394_ff_hdr),
- sizeof(struct eth1394_sf_hdr),
- sizeof(struct eth1394_sf_hdr)
-};
-
-static const u16 eth1394_speedto_maxpayload[] = {
-/* S100, S200, S400, S800, S1600, S3200 */
- 512, 1024, 2048, 4096, 4096, 4096
-};
-
-MODULE_AUTHOR("Ben Collins (bcollins@debian.org)");
-MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)");
-MODULE_LICENSE("GPL");
-
-/*
- * The max_partial_datagrams parameter is the maximum number of fragmented
- * datagrams per node that eth1394 will keep in memory. Providing an upper
- * bound allows us to limit the amount of memory that partial datagrams
- * consume in the event that some partial datagrams are never completed.
- */
-static int max_partial_datagrams = 25;
-module_param(max_partial_datagrams, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(max_partial_datagrams,
- "Maximum number of partially received fragmented datagrams "
- "(default = 25).");
-
-
-static int ether1394_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, const void *daddr,
- const void *saddr, unsigned len);
-static int ether1394_rebuild_header(struct sk_buff *skb);
-static int ether1394_header_parse(const struct sk_buff *skb,
- unsigned char *haddr);
-static int ether1394_header_cache(const struct neighbour *neigh,
- struct hh_cache *hh);
-static void ether1394_header_cache_update(struct hh_cache *hh,
- const struct net_device *dev,
- const unsigned char *haddr);
-static netdev_tx_t ether1394_tx(struct sk_buff *skb,
- struct net_device *dev);
-static void ether1394_iso(struct hpsb_iso *iso);
-
-static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
- quadlet_t *data, u64 addr, size_t len, u16 flags);
-static void ether1394_add_host(struct hpsb_host *host);
-static void ether1394_remove_host(struct hpsb_host *host);
-static void ether1394_host_reset(struct hpsb_host *host);
-
-/* Function for incoming 1394 packets */
-static const struct hpsb_address_ops addr_ops = {
- .write = ether1394_write,
-};
-
-/* Ieee1394 highlevel driver functions */
-static struct hpsb_highlevel eth1394_highlevel = {
- .name = driver_name,
- .add_host = ether1394_add_host,
- .remove_host = ether1394_remove_host,
- .host_reset = ether1394_host_reset,
-};
-
-static int ether1394_recv_init(struct eth1394_priv *priv)
-{
- unsigned int iso_buf_size;
-
- /* FIXME: rawiso limits us to PAGE_SIZE */
- iso_buf_size = min((unsigned int)PAGE_SIZE,
- 2 * (1U << (priv->host->csr.max_rec + 1)));
-
- priv->iso = hpsb_iso_recv_init(priv->host,
- ETHER1394_GASP_BUFFERS * iso_buf_size,
- ETHER1394_GASP_BUFFERS,
- priv->broadcast_channel,
- HPSB_ISO_DMA_PACKET_PER_BUFFER,
- 1, ether1394_iso);
- if (priv->iso == NULL) {
- ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n");
- priv->bc_state = ETHER1394_BC_ERROR;
- return -EAGAIN;
- }
-
- if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
- priv->bc_state = ETHER1394_BC_STOPPED;
- else
- priv->bc_state = ETHER1394_BC_RUNNING;
- return 0;
-}
-
-/* This is called after an "ifup" */
-static int ether1394_open(struct net_device *dev)
-{
- struct eth1394_priv *priv = netdev_priv(dev);
- int ret;
-
- if (priv->bc_state == ETHER1394_BC_ERROR) {
- ret = ether1394_recv_init(priv);
- if (ret)
- return ret;
- }
- netif_start_queue(dev);
- return 0;
-}
-
-/* This is called after an "ifdown" */
-static int ether1394_stop(struct net_device *dev)
-{
- /* flush priv->wake */
- flush_scheduled_work();
-
- netif_stop_queue(dev);
- return 0;
-}
-
-/* FIXME: What to do if we timeout? I think a host reset is probably in order,
- * so that's what we do. Should we increment the stat counters too? */
-static void ether1394_tx_timeout(struct net_device *dev)
-{
- struct hpsb_host *host =
- ((struct eth1394_priv *)netdev_priv(dev))->host;
-
- ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n");
- ether1394_host_reset(host);
-}
-
-static inline int ether1394_max_mtu(struct hpsb_host* host)
-{
- return (1 << (host->csr.max_rec + 1))
- - sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD;
-}
-
-static int ether1394_change_mtu(struct net_device *dev, int new_mtu)
-{
- int max_mtu;
-
- if (new_mtu < 68)
- return -EINVAL;
-
- max_mtu = ether1394_max_mtu(
- ((struct eth1394_priv *)netdev_priv(dev))->host);
- if (new_mtu > max_mtu) {
- ETH1394_PRINT(KERN_INFO, dev->name,
- "Local node constrains MTU to %d\n", max_mtu);
- return -ERANGE;
- }
-
- dev->mtu = new_mtu;
- return 0;
-}
-
-static void purge_partial_datagram(struct list_head *old)
-{
- struct partial_datagram *pd;
- struct list_head *lh, *n;
- struct fragment_info *fi;
-
- pd = list_entry(old, struct partial_datagram, list);
-
- list_for_each_safe(lh, n, &pd->frag_info) {
- fi = list_entry(lh, struct fragment_info, list);
- list_del(lh);
- kfree(fi);
- }
- list_del(old);
- kfree_skb(pd->skb);
- kfree(pd);
-}
-
-/******************************************
- * 1394 bus activity functions
- ******************************************/
-
-static struct eth1394_node_ref *eth1394_find_node(struct list_head *inl,
- struct unit_directory *ud)
-{
- struct eth1394_node_ref *node;
-
- list_for_each_entry(node, inl, list)
- if (node->ud == ud)
- return node;
-
- return NULL;
-}
-
-static struct eth1394_node_ref *eth1394_find_node_guid(struct list_head *inl,
- u64 guid)
-{
- struct eth1394_node_ref *node;
-
- list_for_each_entry(node, inl, list)
- if (node->ud->ne->guid == guid)
- return node;
-
- return NULL;
-}
-
-static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl,
- nodeid_t nodeid)
-{
- struct eth1394_node_ref *node;
-
- list_for_each_entry(node, inl, list)
- if (node->ud->ne->nodeid == nodeid)
- return node;
-
- return NULL;
-}
-
-static int eth1394_new_node(struct eth1394_host_info *hi,
- struct unit_directory *ud)
-{
- struct eth1394_priv *priv;
- struct eth1394_node_ref *new_node;
- struct eth1394_node_info *node_info;
-
- new_node = kmalloc(sizeof(*new_node), GFP_KERNEL);
- if (!new_node)
- return -ENOMEM;
-
- node_info = kmalloc(sizeof(*node_info), GFP_KERNEL);
- if (!node_info) {
- kfree(new_node);
- return -ENOMEM;
- }
-
- spin_lock_init(&node_info->pdg.lock);
- INIT_LIST_HEAD(&node_info->pdg.list);
- node_info->pdg.sz = 0;
- node_info->fifo = CSR1212_INVALID_ADDR_SPACE;
-
- dev_set_drvdata(&ud->device, node_info);
- new_node->ud = ud;
-
- priv = netdev_priv(hi->dev);
- list_add_tail(&new_node->list, &priv->ip_node_list);
- return 0;
-}
-
-static int eth1394_probe(struct device *dev)
-{
- struct unit_directory *ud;
- struct eth1394_host_info *hi;
-
- ud = container_of(dev, struct unit_directory, device);
- hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
- if (!hi)
- return -ENOENT;
-
- return eth1394_new_node(hi, ud);
-}
-
-static int eth1394_remove(struct device *dev)
-{
- struct unit_directory *ud;
- struct eth1394_host_info *hi;
- struct eth1394_priv *priv;
- struct eth1394_node_ref *old_node;
- struct eth1394_node_info *node_info;
- struct list_head *lh, *n;
- unsigned long flags;
-
- ud = container_of(dev, struct unit_directory, device);
- hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
- if (!hi)
- return -ENOENT;
-
- priv = netdev_priv(hi->dev);
-
- old_node = eth1394_find_node(&priv->ip_node_list, ud);
- if (!old_node)
- return 0;
-
- list_del(&old_node->list);
- kfree(old_node);
-
- node_info = dev_get_drvdata(&ud->device);
-
- spin_lock_irqsave(&node_info->pdg.lock, flags);
- /* The partial datagram list should be empty, but we'll just
- * make sure anyway... */
- list_for_each_safe(lh, n, &node_info->pdg.list)
- purge_partial_datagram(lh);
- spin_unlock_irqrestore(&node_info->pdg.lock, flags);
-
- kfree(node_info);
- dev_set_drvdata(&ud->device, NULL);
- return 0;
-}
-
-static int eth1394_update(struct unit_directory *ud)
-{
- struct eth1394_host_info *hi;
- struct eth1394_priv *priv;
- struct eth1394_node_ref *node;
-
- hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
- if (!hi)
- return -ENOENT;
-
- priv = netdev_priv(hi->dev);
- node = eth1394_find_node(&priv->ip_node_list, ud);
- if (node)
- return 0;
-
- return eth1394_new_node(hi, ud);
-}
-
-static const struct ieee1394_device_id eth1394_id_table[] = {
- {
- .match_flags = (IEEE1394_MATCH_SPECIFIER_ID |
- IEEE1394_MATCH_VERSION),
- .specifier_id = ETHER1394_GASP_SPECIFIER_ID,
- .version = ETHER1394_GASP_VERSION,
- },
- {}
-};
-
-MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table);
-
-static struct hpsb_protocol_driver eth1394_proto_driver = {
- .name = driver_name,
- .id_table = eth1394_id_table,
- .update = eth1394_update,
- .driver = {
- .probe = eth1394_probe,
- .remove = eth1394_remove,
- },
-};
-
-static void ether1394_reset_priv(struct net_device *dev, int set_mtu)
-{
- unsigned long flags;
- int i;
- struct eth1394_priv *priv = netdev_priv(dev);
- struct hpsb_host *host = priv->host;
- u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3]));
- int max_speed = IEEE1394_SPEED_MAX;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- memset(priv->ud_list, 0, sizeof(priv->ud_list));
- priv->bc_maxpayload = 512;
-
- /* Determine speed limit */
- /* FIXME: This is broken for nodes with link speed < PHY speed,
- * and it is suboptimal for S200B...S800B hardware.
- * The result of nodemgr's speed probe should be used somehow. */
- for (i = 0; i < host->node_count; i++) {
- /* take care of S100B...S400B PHY ports */
- if (host->speed[i] == SELFID_SPEED_UNKNOWN) {
- max_speed = IEEE1394_SPEED_100;
- break;
- }
- if (max_speed > host->speed[i])
- max_speed = host->speed[i];
- }
- priv->bc_sspd = max_speed;
-
- if (set_mtu) {
- /* Use the RFC 2734 default 1500 octets or the maximum payload
- * as initial MTU */
- dev->mtu = min(1500, ether1394_max_mtu(host));
-
- /* Set our hardware address while we're at it */
- memcpy(dev->dev_addr, &guid, sizeof(u64));
- memset(dev->broadcast, 0xff, sizeof(u64));
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static const struct header_ops ether1394_header_ops = {
- .create = ether1394_header,
- .rebuild = ether1394_rebuild_header,
- .cache = ether1394_header_cache,
- .cache_update = ether1394_header_cache_update,
- .parse = ether1394_header_parse,
-};
-
-static const struct net_device_ops ether1394_netdev_ops = {
- .ndo_open = ether1394_open,
- .ndo_stop = ether1394_stop,
- .ndo_start_xmit = ether1394_tx,
- .ndo_tx_timeout = ether1394_tx_timeout,
- .ndo_change_mtu = ether1394_change_mtu,
-};
-
-static void ether1394_init_dev(struct net_device *dev)
-{
-
- dev->header_ops = &ether1394_header_ops;
- dev->netdev_ops = &ether1394_netdev_ops;
-
- dev->watchdog_timeo = ETHER1394_TIMEOUT;
- dev->flags = IFF_BROADCAST | IFF_MULTICAST;
- dev->features = NETIF_F_HIGHDMA;
- dev->addr_len = ETH1394_ALEN;
- dev->hard_header_len = ETH1394_HLEN;
- dev->type = ARPHRD_IEEE1394;
-
- /* FIXME: This value was copied from ether_setup(). Is it too much? */
- dev->tx_queue_len = 1000;
-}
-
-/*
- * Wake the queue up after commonly encountered transmit failure conditions are
- * hopefully over. Currently only tlabel exhaustion is accounted for.
- */
-static void ether1394_wake_queue(struct work_struct *work)
-{
- struct eth1394_priv *priv;
- struct hpsb_packet *packet;
-
- priv = container_of(work, struct eth1394_priv, wake);
- packet = hpsb_alloc_packet(0);
-
- /* This is really bad, but unjam the queue anyway. */
- if (!packet)
- goto out;
-
- packet->host = priv->host;
- packet->node_id = priv->wake_node;
- /*
- * A transaction label is all we really want. If we get one, it almost
- * always means we can get a lot more because the ieee1394 core recycled
- * a whole batch of tlabels, at last.
- */
- if (hpsb_get_tlabel(packet) == 0)
- hpsb_free_tlabel(packet);
-
- hpsb_free_packet(packet);
-out:
- netif_wake_queue(priv->wake_dev);
-}
-
-/*
- * This function is called every time a card is found. It is generally called
- * when the module is installed. This is where we add all of our ethernet
- * devices. One for each host.
- */
-static void ether1394_add_host(struct hpsb_host *host)
-{
- struct eth1394_host_info *hi = NULL;
- struct net_device *dev = NULL;
- struct eth1394_priv *priv;
- u64 fifo_addr;
-
- if (hpsb_config_rom_ip1394_add(host) != 0) {
- ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n");
- return;
- }
-
- fifo_addr = hpsb_allocate_and_register_addrspace(
- &eth1394_highlevel, host, &addr_ops,
- ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN,
- CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE);
- if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) {
- ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n");
- hpsb_config_rom_ip1394_remove(host);
- return;
- }
-
- dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev);
- if (dev == NULL) {
- ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
- goto out;
- }
-
- SET_NETDEV_DEV(dev, &host->device);
-
- priv = netdev_priv(dev);
- INIT_LIST_HEAD(&priv->ip_node_list);
- spin_lock_init(&priv->lock);
- priv->host = host;
- priv->local_fifo = fifo_addr;
- INIT_WORK(&priv->wake, ether1394_wake_queue);
- priv->wake_dev = dev;
-
- hi = hpsb_create_hostinfo(&eth1394_highlevel, host, sizeof(*hi));
- if (hi == NULL) {
- ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
- goto out;
- }
-
- ether1394_reset_priv(dev, 1);
-
- if (register_netdev(dev)) {
- ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n");
- goto out;
- }
-
- ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n",
- host->id);
-
- hi->host = host;
- hi->dev = dev;
-
- /* Ignore validity in hopes that it will be set in the future. It'll
- * be checked when the eth device is opened. */
- priv->broadcast_channel = host->csr.broadcast_channel & 0x3f;
-
- ether1394_recv_init(priv);
- return;
-out:
- if (dev)
- free_netdev(dev);
- if (hi)
- hpsb_destroy_hostinfo(&eth1394_highlevel, host);
- hpsb_unregister_addrspace(&eth1394_highlevel, host, fifo_addr);
- hpsb_config_rom_ip1394_remove(host);
-}
-
-/* Remove a card from our list */
-static void ether1394_remove_host(struct hpsb_host *host)
-{
- struct eth1394_host_info *hi;
- struct eth1394_priv *priv;
-
- hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
- if (!hi)
- return;
- priv = netdev_priv(hi->dev);
- hpsb_unregister_addrspace(&eth1394_highlevel, host, priv->local_fifo);
- hpsb_config_rom_ip1394_remove(host);
- if (priv->iso)
- hpsb_iso_shutdown(priv->iso);
- unregister_netdev(hi->dev);
- free_netdev(hi->dev);
-}
-
-/* A bus reset happened */
-static void ether1394_host_reset(struct hpsb_host *host)
-{
- struct eth1394_host_info *hi;
- struct eth1394_priv *priv;
- struct net_device *dev;
- struct list_head *lh, *n;
- struct eth1394_node_ref *node;
- struct eth1394_node_info *node_info;
- unsigned long flags;
-
- hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
-
- /* This can happen for hosts that we don't use */
- if (!hi)
- return;
-
- dev = hi->dev;
- priv = netdev_priv(dev);
-
- /* Reset our private host data, but not our MTU */
- netif_stop_queue(dev);
- ether1394_reset_priv(dev, 0);
-
- list_for_each_entry(node, &priv->ip_node_list, list) {
- node_info = dev_get_drvdata(&node->ud->device);
-
- spin_lock_irqsave(&node_info->pdg.lock, flags);
-
- list_for_each_safe(lh, n, &node_info->pdg.list)
- purge_partial_datagram(lh);
-
- INIT_LIST_HEAD(&(node_info->pdg.list));
- node_info->pdg.sz = 0;
-
- spin_unlock_irqrestore(&node_info->pdg.lock, flags);
- }
-
- netif_wake_queue(dev);
-}
-
-/******************************************
- * HW Header net device functions
- ******************************************/
-/* These functions have been adapted from net/ethernet/eth.c */
-
-/* Create a fake MAC header for an arbitrary protocol layer.
- * saddr=NULL means use device source address
- * daddr=NULL means leave destination address (eg unresolved arp). */
-static int ether1394_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, const void *daddr,
- const void *saddr, unsigned len)
-{
- struct eth1394hdr *eth =
- (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN);
-
- eth->h_proto = htons(type);
-
- if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
- memset(eth->h_dest, 0, dev->addr_len);
- return dev->hard_header_len;
- }
-
- if (daddr) {
- memcpy(eth->h_dest, daddr, dev->addr_len);
- return dev->hard_header_len;
- }
-
- return -dev->hard_header_len;
-}
-
-/* Rebuild the faked MAC header. This is called after an ARP
- * (or in future other address resolution) has completed on this
- * sk_buff. We now let ARP fill in the other fields.
- *
- * This routine CANNOT use cached dst->neigh!
- * Really, it is used only when dst->neigh is wrong.
- */
-static int ether1394_rebuild_header(struct sk_buff *skb)
-{
- struct eth1394hdr *eth = (struct eth1394hdr *)skb->data;
-
- if (eth->h_proto == htons(ETH_P_IP))
- return arp_find((unsigned char *)&eth->h_dest, skb);
-
- ETH1394_PRINT(KERN_DEBUG, skb->dev->name,
- "unable to resolve type %04x addresses\n",
- ntohs(eth->h_proto));
- return 0;
-}
-
-static int ether1394_header_parse(const struct sk_buff *skb,
- unsigned char *haddr)
-{
- memcpy(haddr, skb->dev->dev_addr, ETH1394_ALEN);
- return ETH1394_ALEN;
-}
-
-static int ether1394_header_cache(const struct neighbour *neigh,
- struct hh_cache *hh)
-{
- __be16 type = hh->hh_type;
- struct net_device *dev = neigh->dev;
- struct eth1394hdr *eth =
- (struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN);
-
- if (type == htons(ETH_P_802_3))
- return -1;
-
- eth->h_proto = type;
- memcpy(eth->h_dest, neigh->ha, dev->addr_len);
-
- hh->hh_len = ETH1394_HLEN;
- return 0;
-}
-
-/* Called by Address Resolution module to notify changes in address. */
-static void ether1394_header_cache_update(struct hh_cache *hh,
- const struct net_device *dev,
- const unsigned char * haddr)
-{
- memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len);
-}
-
-/******************************************
- * Datagram reception code
- ******************************************/
-
-/* Copied from net/ethernet/eth.c */
-static __be16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev)
-{
- struct eth1394hdr *eth;
- unsigned char *rawp;
-
- skb_reset_mac_header(skb);
- skb_pull(skb, ETH1394_HLEN);
- eth = eth1394_hdr(skb);
-
- if (*eth->h_dest & 1) {
- if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0)
- skb->pkt_type = PACKET_BROADCAST;
-#if 0
- else
- skb->pkt_type = PACKET_MULTICAST;
-#endif
- } else {
- if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len))
- skb->pkt_type = PACKET_OTHERHOST;
- }
-
- if (ntohs(eth->h_proto) >= 1536)
- return eth->h_proto;
-
- rawp = skb->data;
-
- if (*(unsigned short *)rawp == 0xFFFF)
- return htons(ETH_P_802_3);
-
- return htons(ETH_P_802_2);
-}
-
-/* Parse an encapsulated IP1394 header into an ethernet frame packet.
- * We also perform ARP translation here, if need be. */
-static __be16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev,
- nodeid_t srcid, nodeid_t destid,
- __be16 ether_type)
-{
- struct eth1394_priv *priv = netdev_priv(dev);
- __be64 dest_hw;
- __be16 ret = 0;
-
- /* Setup our hw addresses. We use these to build the ethernet header. */
- if (destid == (LOCAL_BUS | ALL_NODES))
- dest_hw = ~cpu_to_be64(0); /* broadcast */
- else
- dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 |
- priv->host->csr.guid_lo);
-
- /* If this is an ARP packet, convert it. First, we want to make
- * use of some of the fields, since they tell us a little bit
- * about the sending machine. */
- if (ether_type == htons(ETH_P_ARP)) {
- struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data;
- struct arphdr *arp = (struct arphdr *)skb->data;
- unsigned char *arp_ptr = (unsigned char *)(arp + 1);
- u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 |
- ntohl(arp1394->fifo_lo);
- u8 max_rec = min(priv->host->csr.max_rec,
- (u8)(arp1394->max_rec));
- int sspd = arp1394->sspd;
- u16 maxpayload;
- struct eth1394_node_ref *node;
- struct eth1394_node_info *node_info;
- __be64 guid;
-
- /* Sanity check. MacOSX seems to be sending us 131 in this
- * field (atleast on my Panther G5). Not sure why. */
- if (sspd > 5 || sspd < 0)
- sspd = 0;
-
- maxpayload = min(eth1394_speedto_maxpayload[sspd],
- (u16)(1 << (max_rec + 1)));
-
- guid = get_unaligned(&arp1394->s_uniq_id);
- node = eth1394_find_node_guid(&priv->ip_node_list,
- be64_to_cpu(guid));
- if (!node)
- return cpu_to_be16(0);
-
- node_info = dev_get_drvdata(&node->ud->device);
-
- /* Update our speed/payload/fifo_offset table */
- node_info->maxpayload = maxpayload;
- node_info->sspd = sspd;
- node_info->fifo = fifo_addr;
-
- /* Now that we're done with the 1394 specific stuff, we'll
- * need to alter some of the data. Believe it or not, all
- * that needs to be done is sender_IP_address needs to be
- * moved, the destination hardware address get stuffed
- * in and the hardware address length set to 8.
- *
- * IMPORTANT: The code below overwrites 1394 specific data
- * needed above so keep the munging of the data for the
- * higher level IP stack last. */
-
- arp->ar_hln = 8;
- arp_ptr += arp->ar_hln; /* skip over sender unique id */
- *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */
- arp_ptr += arp->ar_pln; /* skip over sender IP addr */
-
- if (arp->ar_op == htons(ARPOP_REQUEST))
- memset(arp_ptr, 0, sizeof(u64));
- else
- memcpy(arp_ptr, dev->dev_addr, sizeof(u64));
- }
-
- /* Now add the ethernet header. */
- if (dev_hard_header(skb, dev, ntohs(ether_type), &dest_hw, NULL,
- skb->len) >= 0)
- ret = ether1394_type_trans(skb, dev);
-
- return ret;
-}
-
-static int fragment_overlap(struct list_head *frag_list, int offset, int len)
-{
- struct fragment_info *fi;
- int end = offset + len;
-
- list_for_each_entry(fi, frag_list, list)
- if (offset < fi->offset + fi->len && end > fi->offset)
- return 1;
-
- return 0;
-}
-
-static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl)
-{
- struct partial_datagram *pd;
-
- list_for_each_entry(pd, pdgl, list)
- if (pd->dgl == dgl)
- return &pd->list;
-
- return NULL;
-}
-
-/* Assumes that new fragment does not overlap any existing fragments */
-static int new_fragment(struct list_head *frag_info, int offset, int len)
-{
- struct list_head *lh;
- struct fragment_info *fi, *fi2, *new;
-
- list_for_each(lh, frag_info) {
- fi = list_entry(lh, struct fragment_info, list);
- if (fi->offset + fi->len == offset) {
- /* The new fragment can be tacked on to the end */
- fi->len += len;
- /* Did the new fragment plug a hole? */
- fi2 = list_entry(lh->next, struct fragment_info, list);
- if (fi->offset + fi->len == fi2->offset) {
- /* glue fragments together */
- fi->len += fi2->len;
- list_del(lh->next);
- kfree(fi2);
- }
- return 0;
- } else if (offset + len == fi->offset) {
- /* The new fragment can be tacked on to the beginning */
- fi->offset = offset;
- fi->len += len;
- /* Did the new fragment plug a hole? */
- fi2 = list_entry(lh->prev, struct fragment_info, list);
- if (fi2->offset + fi2->len == fi->offset) {
- /* glue fragments together */
- fi2->len += fi->len;
- list_del(lh);
- kfree(fi);
- }
- return 0;
- } else if (offset > fi->offset + fi->len) {
- break;
- } else if (offset + len < fi->offset) {
- lh = lh->prev;
- break;
- }
- }
-
- new = kmalloc(sizeof(*new), GFP_ATOMIC);
- if (!new)
- return -ENOMEM;
-
- new->offset = offset;
- new->len = len;
-
- list_add(&new->list, lh);
- return 0;
-}
-
-static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl,
- int dgl, int dg_size, char *frag_buf,
- int frag_off, int frag_len)
-{
- struct partial_datagram *new;
-
- new = kmalloc(sizeof(*new), GFP_ATOMIC);
- if (!new)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&new->frag_info);
-
- if (new_fragment(&new->frag_info, frag_off, frag_len) < 0) {
- kfree(new);
- return -ENOMEM;
- }
-
- new->dgl = dgl;
- new->dg_size = dg_size;
-
- new->skb = dev_alloc_skb(dg_size + dev->hard_header_len + 15);
- if (!new->skb) {
- struct fragment_info *fi = list_entry(new->frag_info.next,
- struct fragment_info,
- list);
- kfree(fi);
- kfree(new);
- return -ENOMEM;
- }
-
- skb_reserve(new->skb, (dev->hard_header_len + 15) & ~15);
- new->pbuf = skb_put(new->skb, dg_size);
- memcpy(new->pbuf + frag_off, frag_buf, frag_len);
-
- list_add(&new->list, pdgl);
- return 0;
-}
-
-static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh,
- char *frag_buf, int frag_off, int frag_len)
-{
- struct partial_datagram *pd =
- list_entry(lh, struct partial_datagram, list);
-
- if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0)
- return -ENOMEM;
-
- memcpy(pd->pbuf + frag_off, frag_buf, frag_len);
-
- /* Move list entry to beginnig of list so that oldest partial
- * datagrams percolate to the end of the list */
- list_move(lh, pdgl);
- return 0;
-}
-
-static int is_datagram_complete(struct list_head *lh, int dg_size)
-{
- struct partial_datagram *pd;
- struct fragment_info *fi;
-
- pd = list_entry(lh, struct partial_datagram, list);
- fi = list_entry(pd->frag_info.next, struct fragment_info, list);
-
- return (fi->len == dg_size);
-}
-
-/* Packet reception. We convert the IP1394 encapsulation header to an
- * ethernet header, and fill it with some of our other fields. This is
- * an incoming packet from the 1394 bus. */
-static int ether1394_data_handler(struct net_device *dev, int srcid, int destid,
- char *buf, int len)
-{
- struct sk_buff *skb;
- unsigned long flags;
- struct eth1394_priv *priv = netdev_priv(dev);
- union eth1394_hdr *hdr = (union eth1394_hdr *)buf;
- __be16 ether_type = cpu_to_be16(0); /* initialized to clear warning */
- int hdr_len;
- struct unit_directory *ud = priv->ud_list[NODEID_TO_NODE(srcid)];
- struct eth1394_node_info *node_info;
-
- if (!ud) {
- struct eth1394_node_ref *node;
- node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid);
- if (unlikely(!node)) {
- HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid "
- "lookup failure: " NODE_BUS_FMT,
- NODE_BUS_ARGS(priv->host, srcid));
- dev->stats.rx_dropped++;
- return -1;
- }
- ud = node->ud;
-
- priv->ud_list[NODEID_TO_NODE(srcid)] = ud;
- }
-
- node_info = dev_get_drvdata(&ud->device);
-
- /* First, did we receive a fragmented or unfragmented datagram? */
- hdr->words.word1 = ntohs(hdr->words.word1);
-
- hdr_len = hdr_type_len[hdr->common.lf];
-
- if (hdr->common.lf == ETH1394_HDR_LF_UF) {
- /* An unfragmented datagram has been received by the ieee1394
- * bus. Build an skbuff around it so we can pass it to the
- * high level network layer. */
-
- skb = dev_alloc_skb(len + dev->hard_header_len + 15);
- if (unlikely(!skb)) {
- ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
- dev->stats.rx_dropped++;
- return -1;
- }
- skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
- memcpy(skb_put(skb, len - hdr_len), buf + hdr_len,
- len - hdr_len);
- ether_type = hdr->uf.ether_type;
- } else {
- /* A datagram fragment has been received, now the fun begins. */
-
- struct list_head *pdgl, *lh;
- struct partial_datagram *pd;
- int fg_off;
- int fg_len = len - hdr_len;
- int dg_size;
- int dgl;
- int retval;
- struct pdg_list *pdg = &(node_info->pdg);
-
- hdr->words.word3 = ntohs(hdr->words.word3);
- /* The 4th header word is reserved so no need to do ntohs() */
-
- if (hdr->common.lf == ETH1394_HDR_LF_FF) {
- ether_type = hdr->ff.ether_type;
- dgl = hdr->ff.dgl;
- dg_size = hdr->ff.dg_size + 1;
- fg_off = 0;
- } else {
- hdr->words.word2 = ntohs(hdr->words.word2);
- dgl = hdr->sf.dgl;
- dg_size = hdr->sf.dg_size + 1;
- fg_off = hdr->sf.fg_off;
- }
- spin_lock_irqsave(&pdg->lock, flags);
-
- pdgl = &(pdg->list);
- lh = find_partial_datagram(pdgl, dgl);
-
- if (lh == NULL) {
- while (pdg->sz >= max_partial_datagrams) {
- /* remove the oldest */
- purge_partial_datagram(pdgl->prev);
- pdg->sz--;
- }
-
- retval = new_partial_datagram(dev, pdgl, dgl, dg_size,
- buf + hdr_len, fg_off,
- fg_len);
- if (retval < 0) {
- spin_unlock_irqrestore(&pdg->lock, flags);
- goto bad_proto;
- }
- pdg->sz++;
- lh = find_partial_datagram(pdgl, dgl);
- } else {
- pd = list_entry(lh, struct partial_datagram, list);
-
- if (fragment_overlap(&pd->frag_info, fg_off, fg_len)) {
- /* Overlapping fragments, obliterate old
- * datagram and start new one. */
- purge_partial_datagram(lh);
- retval = new_partial_datagram(dev, pdgl, dgl,
- dg_size,
- buf + hdr_len,
- fg_off, fg_len);
- if (retval < 0) {
- pdg->sz--;
- spin_unlock_irqrestore(&pdg->lock, flags);
- goto bad_proto;
- }
- } else {
- retval = update_partial_datagram(pdgl, lh,
- buf + hdr_len,
- fg_off, fg_len);
- if (retval < 0) {
- /* Couldn't save off fragment anyway
- * so might as well obliterate the
- * datagram now. */
- purge_partial_datagram(lh);
- pdg->sz--;
- spin_unlock_irqrestore(&pdg->lock, flags);
- goto bad_proto;
- }
- } /* fragment overlap */
- } /* new datagram or add to existing one */
-
- pd = list_entry(lh, struct partial_datagram, list);
-
- if (hdr->common.lf == ETH1394_HDR_LF_FF)
- pd->ether_type = ether_type;
-
- if (is_datagram_complete(lh, dg_size)) {
- ether_type = pd->ether_type;
- pdg->sz--;
- skb = skb_get(pd->skb);
- purge_partial_datagram(lh);
- spin_unlock_irqrestore(&pdg->lock, flags);
- } else {
- /* Datagram is not complete, we're done for the
- * moment. */
- spin_unlock_irqrestore(&pdg->lock, flags);
- return 0;
- }
- } /* unframgented datagram or fragmented one */
-
- /* Write metadata, and then pass to the receive level */
- skb->dev = dev;
- skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
-
- /* Parse the encapsulation header. This actually does the job of
- * converting to an ethernet frame header, aswell as arp
- * conversion if needed. ARP conversion is easier in this
- * direction, since we are using ethernet as our backend. */
- skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid,
- ether_type);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- if (!skb->protocol) {
- dev->stats.rx_errors++;
- dev->stats.rx_dropped++;
- dev_kfree_skb_any(skb);
- } else if (netif_rx(skb) == NET_RX_DROP) {
- dev->stats.rx_errors++;
- dev->stats.rx_dropped++;
- } else {
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
-bad_proto:
- if (netif_queue_stopped(dev))
- netif_wake_queue(dev);
-
- return 0;
-}
-
-static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
- quadlet_t *data, u64 addr, size_t len, u16 flags)
-{
- struct eth1394_host_info *hi;
-
- hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
- if (unlikely(!hi)) {
- ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n",
- host->id);
- return RCODE_ADDRESS_ERROR;
- }
-
- if (ether1394_data_handler(hi->dev, srcid, destid, (char*)data, len))
- return RCODE_ADDRESS_ERROR;
- else
- return RCODE_COMPLETE;
-}
-
-static void ether1394_iso(struct hpsb_iso *iso)
-{
- __be32 *data;
- char *buf;
- struct eth1394_host_info *hi;
- struct net_device *dev;
- unsigned int len;
- u32 specifier_id;
- u16 source_id;
- int i;
- int nready;
-
- hi = hpsb_get_hostinfo(&eth1394_highlevel, iso->host);
- if (unlikely(!hi)) {
- ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n",
- iso->host->id);
- return;
- }
-
- dev = hi->dev;
-
- nready = hpsb_iso_n_ready(iso);
- for (i = 0; i < nready; i++) {
- struct hpsb_iso_packet_info *info =
- &iso->infos[(iso->first_packet + i) % iso->buf_packets];
- data = (__be32 *)(iso->data_buf.kvirt + info->offset);
-
- /* skip over GASP header */
- buf = (char *)data + 8;
- len = info->len - 8;
-
- specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 |
- (be32_to_cpu(data[1]) & 0xff000000) >> 24;
- source_id = be32_to_cpu(data[0]) >> 16;
-
- if (info->channel != (iso->host->csr.broadcast_channel & 0x3f)
- || specifier_id != ETHER1394_GASP_SPECIFIER_ID) {
- /* This packet is not for us */
- continue;
- }
- ether1394_data_handler(dev, source_id, LOCAL_BUS | ALL_NODES,
- buf, len);
- }
-
- hpsb_iso_recv_release_packets(iso, i);
-
-}
-
-/******************************************
- * Datagram transmission code
- ******************************************/
-
-/* Convert a standard ARP packet to 1394 ARP. The first 8 bytes (the entire
- * arphdr) is the same format as the ip1394 header, so they overlap. The rest
- * needs to be munged a bit. The remainder of the arphdr is formatted based
- * on hwaddr len and ipaddr len. We know what they'll be, so it's easy to
- * judge.
- *
- * Now that the EUI is used for the hardware address all we need to do to make
- * this work for 1394 is to insert 2 quadlets that contain max_rec size,
- * speed, and unicast FIFO address information between the sender_unique_id
- * and the IP addresses.
- */
-static void ether1394_arp_to_1394arp(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct eth1394_priv *priv = netdev_priv(dev);
- struct arphdr *arp = (struct arphdr *)skb->data;
- unsigned char *arp_ptr = (unsigned char *)(arp + 1);
- struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data;
-
- arp1394->hw_addr_len = 16;
- arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN);
- arp1394->max_rec = priv->host->csr.max_rec;
- arp1394->sspd = priv->host->csr.lnk_spd;
- arp1394->fifo_hi = htons(priv->local_fifo >> 32);
- arp1394->fifo_lo = htonl(priv->local_fifo & ~0x0);
-}
-
-/* We need to encapsulate the standard header with our own. We use the
- * ethernet header's proto for our own. */
-static unsigned int ether1394_encapsulate_prep(unsigned int max_payload,
- __be16 proto,
- union eth1394_hdr *hdr,
- u16 dg_size, u16 dgl)
-{
- unsigned int adj_max_payload =
- max_payload - hdr_type_len[ETH1394_HDR_LF_UF];
-
- /* Does it all fit in one packet? */
- if (dg_size <= adj_max_payload) {
- hdr->uf.lf = ETH1394_HDR_LF_UF;
- hdr->uf.ether_type = proto;
- } else {
- hdr->ff.lf = ETH1394_HDR_LF_FF;
- hdr->ff.ether_type = proto;
- hdr->ff.dg_size = dg_size - 1;
- hdr->ff.dgl = dgl;
- adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF];
- }
- return DIV_ROUND_UP(dg_size, adj_max_payload);
-}
-
-static unsigned int ether1394_encapsulate(struct sk_buff *skb,
- unsigned int max_payload,
- union eth1394_hdr *hdr)
-{
- union eth1394_hdr *bufhdr;
- int ftype = hdr->common.lf;
- int hdrsz = hdr_type_len[ftype];
- unsigned int adj_max_payload = max_payload - hdrsz;
-
- switch (ftype) {
- case ETH1394_HDR_LF_UF:
- bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz);
- bufhdr->words.word1 = htons(hdr->words.word1);
- bufhdr->words.word2 = hdr->words.word2;
- break;
-
- case ETH1394_HDR_LF_FF:
- bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz);
- bufhdr->words.word1 = htons(hdr->words.word1);
- bufhdr->words.word2 = hdr->words.word2;
- bufhdr->words.word3 = htons(hdr->words.word3);
- bufhdr->words.word4 = 0;
-
- /* Set frag type here for future interior fragments */
- hdr->common.lf = ETH1394_HDR_LF_IF;
- hdr->sf.fg_off = 0;
- break;
-
- default:
- hdr->sf.fg_off += adj_max_payload;
- bufhdr = (union eth1394_hdr *)skb_pull(skb, adj_max_payload);
- if (max_payload >= skb->len)
- hdr->common.lf = ETH1394_HDR_LF_LF;
- bufhdr->words.word1 = htons(hdr->words.word1);
- bufhdr->words.word2 = htons(hdr->words.word2);
- bufhdr->words.word3 = htons(hdr->words.word3);
- bufhdr->words.word4 = 0;
- }
- return min(max_payload, skb->len);
-}
-
-static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host)
-{
- struct hpsb_packet *p;
-
- p = hpsb_alloc_packet(0);
- if (p) {
- p->host = host;
- p->generation = get_hpsb_generation(host);
- p->type = hpsb_async;
- }
- return p;
-}
-
-static int ether1394_prep_write_packet(struct hpsb_packet *p,
- struct hpsb_host *host, nodeid_t node,
- u64 addr, void *data, int tx_len)
-{
- p->node_id = node;
-
- if (hpsb_get_tlabel(p))
- return -EAGAIN;
-
- p->tcode = TCODE_WRITEB;
- p->header_size = 16;
- p->expect_response = 1;
- p->header[0] =
- p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4;
- p->header[1] = host->node_id << 16 | addr >> 32;
- p->header[2] = addr & 0xffffffff;
- p->header[3] = tx_len << 16;
- p->data_size = (tx_len + 3) & ~3;
- p->data = data;
-
- return 0;
-}
-
-static void ether1394_prep_gasp_packet(struct hpsb_packet *p,
- struct eth1394_priv *priv,
- struct sk_buff *skb, int length)
-{
- p->header_size = 4;
- p->tcode = TCODE_STREAM_DATA;
-
- p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 |
- TCODE_STREAM_DATA << 4;
- p->data_size = length;
- p->data = (quadlet_t *)skb->data - 2;
- p->data[0] = cpu_to_be32(priv->host->node_id << 16 |
- ETHER1394_GASP_SPECIFIER_ID_HI);
- p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 |
- ETHER1394_GASP_VERSION);
-
- p->speed_code = priv->bc_sspd;
-
- /* prevent hpsb_send_packet() from overriding our speed code */
- p->node_id = LOCAL_BUS | ALL_NODES;
-}
-
-static void ether1394_free_packet(struct hpsb_packet *packet)
-{
- if (packet->tcode != TCODE_STREAM_DATA)
- hpsb_free_tlabel(packet);
- hpsb_free_packet(packet);
-}
-
-static void ether1394_complete_cb(void *__ptask);
-
-static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len)
-{
- struct eth1394_priv *priv = ptask->priv;
- struct hpsb_packet *packet = NULL;
-
- packet = ether1394_alloc_common_packet(priv->host);
- if (!packet)
- return -ENOMEM;
-
- if (ptask->tx_type == ETH1394_GASP) {
- int length = tx_len + 2 * sizeof(quadlet_t);
-
- ether1394_prep_gasp_packet(packet, priv, ptask->skb, length);
- } else if (ether1394_prep_write_packet(packet, priv->host,
- ptask->dest_node,
- ptask->addr, ptask->skb->data,
- tx_len)) {
- hpsb_free_packet(packet);
- return -EAGAIN;
- }
-
- ptask->packet = packet;
- hpsb_set_packet_complete_task(ptask->packet, ether1394_complete_cb,
- ptask);
-
- if (hpsb_send_packet(packet) < 0) {
- ether1394_free_packet(packet);
- return -EIO;
- }
-
- return 0;
-}
-
-/* Task function to be run when a datagram transmission is completed */
-static void ether1394_dg_complete(struct packet_task *ptask, int fail)
-{
- struct sk_buff *skb = ptask->skb;
- struct net_device *dev = skb->dev;
- struct eth1394_priv *priv = netdev_priv(dev);
- unsigned long flags;
-
- /* Statistics */
- spin_lock_irqsave(&priv->lock, flags);
- if (fail) {
- dev->stats.tx_dropped++;
- dev->stats.tx_errors++;
- } else {
- dev->stats.tx_bytes += skb->len;
- dev->stats.tx_packets++;
- }
- spin_unlock_irqrestore(&priv->lock, flags);
-
- dev_kfree_skb_any(skb);
- kmem_cache_free(packet_task_cache, ptask);
-}
-
-/* Callback for when a packet has been sent and the status of that packet is
- * known */
-static void ether1394_complete_cb(void *__ptask)
-{
- struct packet_task *ptask = (struct packet_task *)__ptask;
- struct hpsb_packet *packet = ptask->packet;
- int fail = 0;
-
- if (packet->tcode != TCODE_STREAM_DATA)
- fail = hpsb_packet_success(packet);
-
- ether1394_free_packet(packet);
-
- ptask->outstanding_pkts--;
- if (ptask->outstanding_pkts > 0 && !fail) {
- int tx_len, err;
-
- /* Add the encapsulation header to the fragment */
- tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload,
- &ptask->hdr);
- err = ether1394_send_packet(ptask, tx_len);
- if (err) {
- if (err == -EAGAIN)
- ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n");
-
- ether1394_dg_complete(ptask, 1);
- }
- } else {
- ether1394_dg_complete(ptask, fail);
- }
-}
-
-/* Transmit a packet (called by kernel) */
-static netdev_tx_t ether1394_tx(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct eth1394hdr hdr_buf;
- struct eth1394_priv *priv = netdev_priv(dev);
- __be16 proto;
- unsigned long flags;
- nodeid_t dest_node;
- eth1394_tx_type tx_type;
- unsigned int tx_len;
- unsigned int max_payload;
- u16 dg_size;
- u16 dgl;
- struct packet_task *ptask;
- struct eth1394_node_ref *node;
- struct eth1394_node_info *node_info = NULL;
-
- ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC);
- if (ptask == NULL)
- goto fail;
-
- /* XXX Ignore this for now. Noticed that when MacOSX is the IRM,
- * it does not set our validity bit. We need to compensate for
- * that somewhere else, but not in eth1394. */
-#if 0
- if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000)
- goto fail;
-#endif
-
- skb = skb_share_check(skb, GFP_ATOMIC);
- if (!skb)
- goto fail;
-
- /* Get rid of the fake eth1394 header, but first make a copy.
- * We might need to rebuild the header on tx failure. */
- memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
- skb_pull(skb, ETH1394_HLEN);
-
- proto = hdr_buf.h_proto;
- dg_size = skb->len;
-
- /* Set the transmission type for the packet. ARP packets and IP
- * broadcast packets are sent via GASP. */
- if (memcmp(hdr_buf.h_dest, dev->broadcast, ETH1394_ALEN) == 0 ||
- proto == htons(ETH_P_ARP) ||
- (proto == htons(ETH_P_IP) &&
- IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
- tx_type = ETH1394_GASP;
- dest_node = LOCAL_BUS | ALL_NODES;
- max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD;
- BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);
- dgl = priv->bc_dgl;
- if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
- priv->bc_dgl++;
- } else {
- __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest);
-
- node = eth1394_find_node_guid(&priv->ip_node_list,
- be64_to_cpu(guid));
- if (!node)
- goto fail;
-
- node_info = dev_get_drvdata(&node->ud->device);
- if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE)
- goto fail;
-
- dest_node = node->ud->ne->nodeid;
- max_payload = node_info->maxpayload;
- BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);
-
- dgl = node_info->dgl;
- if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
- node_info->dgl++;
- tx_type = ETH1394_WRREQ;
- }
-
- /* If this is an ARP packet, convert it */
- if (proto == htons(ETH_P_ARP))
- ether1394_arp_to_1394arp(skb, dev);
-
- ptask->hdr.words.word1 = 0;
- ptask->hdr.words.word2 = 0;
- ptask->hdr.words.word3 = 0;
- ptask->hdr.words.word4 = 0;
- ptask->skb = skb;
- ptask->priv = priv;
- ptask->tx_type = tx_type;
-
- if (tx_type != ETH1394_GASP) {
- u64 addr;
-
- spin_lock_irqsave(&priv->lock, flags);
- addr = node_info->fifo;
- spin_unlock_irqrestore(&priv->lock, flags);
-
- ptask->addr = addr;
- ptask->dest_node = dest_node;
- }
-
- ptask->tx_type = tx_type;
- ptask->max_payload = max_payload;
- ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload,
- proto, &ptask->hdr, dg_size, dgl);
-
- /* Add the encapsulation header to the fragment */
- tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr);
- dev->trans_start = jiffies;
- if (ether1394_send_packet(ptask, tx_len)) {
- if (dest_node == (LOCAL_BUS | ALL_NODES))
- goto fail;
-
- /* At this point we want to restore the packet. When we return
- * here with NETDEV_TX_BUSY we will get another entrance in this
- * routine with the same skb and we need it to look the same.
- * So we pull 4 more bytes, then build the header again. */
- skb_pull(skb, 4);
- ether1394_header(skb, dev, ntohs(hdr_buf.h_proto),
- hdr_buf.h_dest, NULL, 0);
-
- /* Most failures of ether1394_send_packet are recoverable. */
- netif_stop_queue(dev);
- priv->wake_node = dest_node;
- schedule_work(&priv->wake);
- kmem_cache_free(packet_task_cache, ptask);
- return NETDEV_TX_BUSY;
- }
-
- return NETDEV_TX_OK;
-fail:
- if (ptask)
- kmem_cache_free(packet_task_cache, ptask);
-
- if (skb != NULL)
- dev_kfree_skb(skb);
-
- spin_lock_irqsave(&priv->lock, flags);
- dev->stats.tx_dropped++;
- dev->stats.tx_errors++;
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return NETDEV_TX_OK;
-}
-
-static int __init ether1394_init_module(void)
-{
- int err;
-
- packet_task_cache = kmem_cache_create("packet_task",
- sizeof(struct packet_task),
- 0, 0, NULL);
- if (!packet_task_cache)
- return -ENOMEM;
-
- hpsb_register_highlevel(&eth1394_highlevel);
- err = hpsb_register_protocol(&eth1394_proto_driver);
- if (err) {
- hpsb_unregister_highlevel(&eth1394_highlevel);
- kmem_cache_destroy(packet_task_cache);
- }
- return err;
-}
-
-static void __exit ether1394_exit_module(void)
-{
- hpsb_unregister_protocol(&eth1394_proto_driver);
- hpsb_unregister_highlevel(&eth1394_highlevel);
- kmem_cache_destroy(packet_task_cache);
-}
-
-module_init(ether1394_init_module);
-module_exit(ether1394_exit_module);
diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h
deleted file mode 100644
index d53bac47b86f..000000000000
--- a/drivers/ieee1394/eth1394.h
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * eth1394.h -- Ethernet driver for Linux IEEE-1394 Subsystem
- *
- * Copyright (C) 2000 Bonin Franck <boninf@free.fr>
- * (C) 2001 Ben Collins <bcollins@debian.org>
- *
- * Mainly based on work by Emanuel Pirker and Andreas E. Bombe
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __ETH1394_H
-#define __ETH1394_H
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <asm/byteorder.h>
-
-#include "ieee1394.h"
-#include "ieee1394_types.h"
-
-/* Register for incoming packets. This is 4096 bytes, which supports up to
- * S3200 (per Table 16-3 of IEEE 1394b-2002). */
-#define ETHER1394_REGION_ADDR_LEN 4096
-
-/* GASP identifier numbers for IPv4 over IEEE 1394 */
-#define ETHER1394_GASP_SPECIFIER_ID 0x00005E
-#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff)
-#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff)
-#define ETHER1394_GASP_VERSION 1
-
-#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */
-
-#define ETHER1394_GASP_BUFFERS 16
-
-#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */
-
-enum eth1394_bc_states { ETHER1394_BC_ERROR,
- ETHER1394_BC_RUNNING,
- ETHER1394_BC_STOPPED };
-
-
-/* Private structure for our ethernet driver */
-struct eth1394_priv {
- struct hpsb_host *host; /* The card for this dev */
- u16 bc_maxpayload; /* Max broadcast payload */
- u8 bc_sspd; /* Max broadcast speed */
- u64 local_fifo; /* Local FIFO Address */
- spinlock_t lock; /* Private lock */
- int broadcast_channel; /* Async stream Broadcast Channel */
- enum eth1394_bc_states bc_state; /* broadcast channel state */
- struct hpsb_iso *iso; /* Async stream recv handle */
- int bc_dgl; /* Outgoing broadcast datagram label */
- struct list_head ip_node_list; /* List of IP capable nodes */
- struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */
-
- struct work_struct wake; /* Wake up after xmit failure */
- struct net_device *wake_dev; /* Stupid backlink for .wake */
- nodeid_t wake_node; /* Destination of failed xmit */
-};
-
-
-/* Define a fake hardware header format for the networking core. Note that
- * header size cannot exceed 16 bytes as that is the size of the header cache.
- * Also, we do not need the source address in the header so we omit it and
- * keep the header to under 16 bytes */
-#define ETH1394_ALEN (8)
-#define ETH1394_HLEN (10)
-
-struct eth1394hdr {
- unsigned char h_dest[ETH1394_ALEN]; /* destination eth1394 addr */
- __be16 h_proto; /* packet type ID field */
-} __attribute__((packed));
-
-static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb)
-{
- return (struct eth1394hdr *)skb_mac_header(skb);
-}
-
-typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type;
-
-/* IP1394 headers */
-
-/* Unfragmented */
-#if defined __BIG_ENDIAN_BITFIELD
-struct eth1394_uf_hdr {
- u16 lf:2;
- u16 res:14;
- __be16 ether_type; /* Ethernet packet type */
-} __attribute__((packed));
-#elif defined __LITTLE_ENDIAN_BITFIELD
-struct eth1394_uf_hdr {
- u16 res:14;
- u16 lf:2;
- __be16 ether_type;
-} __attribute__((packed));
-#else
-#error Unknown bit field type
-#endif
-
-/* First fragment */
-#if defined __BIG_ENDIAN_BITFIELD
-struct eth1394_ff_hdr {
- u16 lf:2;
- u16 res1:2;
- u16 dg_size:12; /* Datagram size */
- __be16 ether_type; /* Ethernet packet type */
- u16 dgl; /* Datagram label */
- u16 res2;
-} __attribute__((packed));
-#elif defined __LITTLE_ENDIAN_BITFIELD
-struct eth1394_ff_hdr {
- u16 dg_size:12;
- u16 res1:2;
- u16 lf:2;
- __be16 ether_type;
- u16 dgl;
- u16 res2;
-} __attribute__((packed));
-#else
-#error Unknown bit field type
-#endif
-
-/* XXX: Subsequent fragments, including last */
-#if defined __BIG_ENDIAN_BITFIELD
-struct eth1394_sf_hdr {
- u16 lf:2;
- u16 res1:2;
- u16 dg_size:12; /* Datagram size */
- u16 res2:4;
- u16 fg_off:12; /* Fragment offset */
- u16 dgl; /* Datagram label */
- u16 res3;
-} __attribute__((packed));
-#elif defined __LITTLE_ENDIAN_BITFIELD
-struct eth1394_sf_hdr {
- u16 dg_size:12;
- u16 res1:2;
- u16 lf:2;
- u16 fg_off:12;
- u16 res2:4;
- u16 dgl;
- u16 res3;
-} __attribute__((packed));
-#else
-#error Unknown bit field type
-#endif
-
-#if defined __BIG_ENDIAN_BITFIELD
-struct eth1394_common_hdr {
- u16 lf:2;
- u16 pad1:14;
-} __attribute__((packed));
-#elif defined __LITTLE_ENDIAN_BITFIELD
-struct eth1394_common_hdr {
- u16 pad1:14;
- u16 lf:2;
-} __attribute__((packed));
-#else
-#error Unknown bit field type
-#endif
-
-struct eth1394_hdr_words {
- u16 word1;
- u16 word2;
- u16 word3;
- u16 word4;
-};
-
-union eth1394_hdr {
- struct eth1394_common_hdr common;
- struct eth1394_uf_hdr uf;
- struct eth1394_ff_hdr ff;
- struct eth1394_sf_hdr sf;
- struct eth1394_hdr_words words;
-};
-
-/* End of IP1394 headers */
-
-/* Fragment types */
-#define ETH1394_HDR_LF_UF 0 /* unfragmented */
-#define ETH1394_HDR_LF_FF 1 /* first fragment */
-#define ETH1394_HDR_LF_LF 2 /* last fragment */
-#define ETH1394_HDR_LF_IF 3 /* interior fragment */
-
-#define IP1394_HW_ADDR_LEN 16 /* As per RFC */
-
-/* Our arp packet (ARPHRD_IEEE1394) */
-struct eth1394_arp {
- u16 hw_type; /* 0x0018 */
- u16 proto_type; /* 0x0806 */
- u8 hw_addr_len; /* 16 */
- u8 ip_addr_len; /* 4 */
- u16 opcode; /* ARP Opcode */
- /* Above is exactly the same format as struct arphdr */
-
- __be64 s_uniq_id; /* Sender's 64bit EUI */
- u8 max_rec; /* Sender's max packet size */
- u8 sspd; /* Sender's max speed */
- __be16 fifo_hi; /* hi 16bits of sender's FIFO addr */
- __be32 fifo_lo; /* lo 32bits of sender's FIFO addr */
- u32 sip; /* Sender's IP Address */
- u32 tip; /* IP Address of requested hw addr */
-};
-
-/* Network timeout */
-#define ETHER1394_TIMEOUT 100000
-
-/* This is our task struct. It's used for the packet complete callback. */
-struct packet_task {
- struct sk_buff *skb;
- int outstanding_pkts;
- eth1394_tx_type tx_type;
- int max_payload;
- struct hpsb_packet *packet;
- struct eth1394_priv *priv;
- union eth1394_hdr hdr;
- u64 addr;
- u16 dest_node;
-};
-
-#endif /* __ETH1394_H */
diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c
deleted file mode 100644
index 4bc443546e04..000000000000
--- a/drivers/ieee1394/highlevel.c
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * Copyright (C) 1999 Andreas E. Bombe
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- *
- *
- * Contributions:
- *
- * Christian Toegel <christian.toegel@gmx.at>
- * unregister address space
- *
- * Manfred Weihs <weihs@ict.tuwien.ac.at>
- * unregister address space
- *
- */
-
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/bitops.h>
-
-#include "ieee1394.h"
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "nodemgr.h"
-
-
-struct hl_host_info {
- struct list_head list;
- struct hpsb_host *host;
- size_t size;
- unsigned long key;
- void *data;
-};
-
-
-static LIST_HEAD(hl_drivers);
-static DECLARE_RWSEM(hl_drivers_sem);
-
-static LIST_HEAD(hl_irqs);
-static DEFINE_RWLOCK(hl_irqs_lock);
-
-static DEFINE_RWLOCK(addr_space_lock);
-
-
-static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl,
- struct hpsb_host *host)
-{
- struct hl_host_info *hi = NULL;
-
- if (!hl || !host)
- return NULL;
-
- read_lock(&hl->host_info_lock);
- list_for_each_entry(hi, &hl->host_info_list, list) {
- if (hi->host == host) {
- read_unlock(&hl->host_info_lock);
- return hi;
- }
- }
- read_unlock(&hl->host_info_lock);
- return NULL;
-}
-
-/**
- * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host
- *
- * Returns a per @host and @hl driver data structure that was previously stored
- * by hpsb_create_hostinfo.
- */
-void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
-{
- struct hl_host_info *hi = hl_get_hostinfo(hl, host);
-
- return hi ? hi->data : NULL;
-}
-
-/**
- * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host
- *
- * Allocate a hostinfo pointer backed by memory with @data_size and bind it to
- * to this @hl driver and @host. If @data_size is zero, then the return here is
- * only valid for error checking.
- */
-void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
- size_t data_size)
-{
- struct hl_host_info *hi;
- void *data;
- unsigned long flags;
-
- hi = hl_get_hostinfo(hl, host);
- if (hi) {
- HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already"
- " exists", hl->name);
- return NULL;
- }
-
- hi = kzalloc(sizeof(*hi) + data_size, GFP_ATOMIC);
- if (!hi)
- return NULL;
-
- if (data_size) {
- data = hi->data = hi + 1;
- hi->size = data_size;
- } else
- data = hi;
-
- hi->host = host;
-
- write_lock_irqsave(&hl->host_info_lock, flags);
- list_add_tail(&hi->list, &hl->host_info_list);
- write_unlock_irqrestore(&hl->host_info_lock, flags);
-
- return data;
-}
-
-/**
- * hpsb_set_hostinfo - set the hostinfo pointer to something useful
- *
- * Usually follows a call to hpsb_create_hostinfo, where the size is 0.
- */
-int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
- void *data)
-{
- struct hl_host_info *hi;
-
- hi = hl_get_hostinfo(hl, host);
- if (hi) {
- if (!hi->size && !hi->data) {
- hi->data = data;
- return 0;
- } else
- HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo "
- "already has data", hl->name);
- } else
- HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists",
- hl->name);
- return -EINVAL;
-}
-
-/**
- * hpsb_destroy_hostinfo - free and remove a hostinfo pointer
- *
- * Free and remove the hostinfo pointer bound to this @hl driver and @host.
- */
-void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
-{
- struct hl_host_info *hi;
-
- hi = hl_get_hostinfo(hl, host);
- if (hi) {
- unsigned long flags;
- write_lock_irqsave(&hl->host_info_lock, flags);
- list_del(&hi->list);
- write_unlock_irqrestore(&hl->host_info_lock, flags);
- kfree(hi);
- }
- return;
-}
-
-/**
- * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo
- *
- * Sets an alternate lookup key for the hostinfo bound to this @hl driver and
- * @host.
- */
-void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
- unsigned long key)
-{
- struct hl_host_info *hi;
-
- hi = hl_get_hostinfo(hl, host);
- if (hi)
- hi->key = key;
- return;
-}
-
-/**
- * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key
- */
-void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
-{
- struct hl_host_info *hi;
- void *data = NULL;
-
- if (!hl)
- return NULL;
-
- read_lock(&hl->host_info_lock);
- list_for_each_entry(hi, &hl->host_info_list, list) {
- if (hi->key == key) {
- data = hi->data;
- break;
- }
- }
- read_unlock(&hl->host_info_lock);
- return data;
-}
-
-static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data)
-{
- struct hpsb_highlevel *hl = __data;
-
- hl->add_host(host);
-
- if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
- HPSB_ERR("Failed to generate Configuration ROM image for host "
- "%s-%d", hl->name, host->id);
- return 0;
-}
-
-/**
- * hpsb_register_highlevel - register highlevel driver
- *
- * The name pointer in @hl has to stay valid at all times because the string is
- * not copied.
- */
-void hpsb_register_highlevel(struct hpsb_highlevel *hl)
-{
- unsigned long flags;
-
- hpsb_init_highlevel(hl);
- INIT_LIST_HEAD(&hl->addr_list);
-
- down_write(&hl_drivers_sem);
- list_add_tail(&hl->hl_list, &hl_drivers);
- up_write(&hl_drivers_sem);
-
- write_lock_irqsave(&hl_irqs_lock, flags);
- list_add_tail(&hl->irq_list, &hl_irqs);
- write_unlock_irqrestore(&hl_irqs_lock, flags);
-
- if (hl->add_host)
- nodemgr_for_each_host(hl, highlevel_for_each_host_reg);
- return;
-}
-
-static void __delete_addr(struct hpsb_address_serve *as)
-{
- list_del(&as->host_list);
- list_del(&as->hl_list);
- kfree(as);
-}
-
-static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host,
- int update_cr)
-{
- unsigned long flags;
- struct list_head *lh, *next;
- struct hpsb_address_serve *as;
-
- /* First, let the highlevel driver unreg */
- if (hl->remove_host)
- hl->remove_host(host);
-
- /* Remove any addresses that are matched for this highlevel driver
- * and this particular host. */
- write_lock_irqsave(&addr_space_lock, flags);
- list_for_each_safe (lh, next, &hl->addr_list) {
- as = list_entry(lh, struct hpsb_address_serve, hl_list);
- if (as->host == host)
- __delete_addr(as);
- }
- write_unlock_irqrestore(&addr_space_lock, flags);
-
- /* Now update the config-rom to reflect anything removed by the
- * highlevel driver. */
- if (update_cr && host->update_config_rom &&
- hpsb_update_config_rom_image(host) < 0)
- HPSB_ERR("Failed to generate Configuration ROM image for host "
- "%s-%d", hl->name, host->id);
-
- /* Finally remove all the host info associated between these two. */
- hpsb_destroy_hostinfo(hl, host);
-}
-
-static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data)
-{
- struct hpsb_highlevel *hl = __data;
-
- __unregister_host(hl, host, 1);
- return 0;
-}
-
-/**
- * hpsb_unregister_highlevel - unregister highlevel driver
- */
-void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
-{
- unsigned long flags;
-
- write_lock_irqsave(&hl_irqs_lock, flags);
- list_del(&hl->irq_list);
- write_unlock_irqrestore(&hl_irqs_lock, flags);
-
- down_write(&hl_drivers_sem);
- list_del(&hl->hl_list);
- up_write(&hl_drivers_sem);
-
- nodemgr_for_each_host(hl, highlevel_for_each_host_unreg);
-}
-
-/**
- * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space
- *
- * @start and @end are 48 bit pointers and have to be quadlet aligned.
- * @end points to the first address behind the handled addresses. This
- * function can be called multiple times for a single hpsb_highlevel @hl to
- * implement sparse register sets. The requested region must not overlap any
- * previously allocated region, otherwise registering will fail.
- *
- * It returns true for successful allocation. Address spaces can be
- * unregistered with hpsb_unregister_addrspace. All remaining address spaces
- * are automatically deallocated together with the hpsb_highlevel @hl.
- */
-u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
- struct hpsb_host *host,
- const struct hpsb_address_ops *ops,
- u64 size, u64 alignment,
- u64 start, u64 end)
-{
- struct hpsb_address_serve *as, *a1, *a2;
- struct list_head *entry;
- u64 retval = CSR1212_INVALID_ADDR_SPACE;
- unsigned long flags;
- u64 align_mask = ~(alignment - 1);
-
- if ((alignment & 3) || (alignment > 0x800000000000ULL) ||
- (hweight64(alignment) != 1)) {
- HPSB_ERR("%s called with invalid alignment: 0x%048llx",
- __func__, (unsigned long long)alignment);
- return retval;
- }
-
- /* default range,
- * avoids controller's posted write area (see OHCI 1.1 clause 1.5) */
- if (start == CSR1212_INVALID_ADDR_SPACE &&
- end == CSR1212_INVALID_ADDR_SPACE) {
- start = host->middle_addr_space;
- end = CSR1212_ALL_SPACE_END;
- }
-
- if (((start|end) & ~align_mask) || (start >= end) ||
- (end > CSR1212_ALL_SPACE_END)) {
- HPSB_ERR("%s called with invalid addresses "
- "(start = %012Lx end = %012Lx)", __func__,
- (unsigned long long)start,(unsigned long long)end);
- return retval;
- }
-
- as = kmalloc(sizeof(*as), GFP_KERNEL);
- if (!as)
- return retval;
-
- INIT_LIST_HEAD(&as->host_list);
- INIT_LIST_HEAD(&as->hl_list);
- as->op = ops;
- as->host = host;
-
- write_lock_irqsave(&addr_space_lock, flags);
- list_for_each(entry, &host->addr_space) {
- u64 a1sa, a1ea;
- u64 a2sa, a2ea;
-
- a1 = list_entry(entry, struct hpsb_address_serve, host_list);
- a2 = list_entry(entry->next, struct hpsb_address_serve,
- host_list);
-
- a1sa = a1->start & align_mask;
- a1ea = (a1->end + alignment -1) & align_mask;
- a2sa = a2->start & align_mask;
- a2ea = (a2->end + alignment -1) & align_mask;
-
- if ((a2sa - a1ea >= size) && (a2sa - start >= size) &&
- (a2sa > start)) {
- as->start = max(start, a1ea);
- as->end = as->start + size;
- list_add(&as->host_list, entry);
- list_add_tail(&as->hl_list, &hl->addr_list);
- retval = as->start;
- break;
- }
- }
- write_unlock_irqrestore(&addr_space_lock, flags);
-
- if (retval == CSR1212_INVALID_ADDR_SPACE)
- kfree(as);
- return retval;
-}
-
-/**
- * hpsb_register_addrspace - register a host address space
- *
- * @start and @end are 48 bit pointers and have to be quadlet aligned.
- * @end points to the first address behind the handled addresses. This
- * function can be called multiple times for a single hpsb_highlevel @hl to
- * implement sparse register sets. The requested region must not overlap any
- * previously allocated region, otherwise registering will fail.
- *
- * It returns true for successful allocation. Address spaces can be
- * unregistered with hpsb_unregister_addrspace. All remaining address spaces
- * are automatically deallocated together with the hpsb_highlevel @hl.
- */
-int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- const struct hpsb_address_ops *ops,
- u64 start, u64 end)
-{
- struct hpsb_address_serve *as;
- struct list_head *lh;
- int retval = 0;
- unsigned long flags;
-
- if (((start|end) & 3) || (start >= end) ||
- (end > CSR1212_ALL_SPACE_END)) {
- HPSB_ERR("%s called with invalid addresses", __func__);
- return 0;
- }
-
- as = kmalloc(sizeof(*as), GFP_KERNEL);
- if (!as)
- return 0;
-
- INIT_LIST_HEAD(&as->host_list);
- INIT_LIST_HEAD(&as->hl_list);
- as->op = ops;
- as->start = start;
- as->end = end;
- as->host = host;
-
- write_lock_irqsave(&addr_space_lock, flags);
- list_for_each(lh, &host->addr_space) {
- struct hpsb_address_serve *as_this =
- list_entry(lh, struct hpsb_address_serve, host_list);
- struct hpsb_address_serve *as_next =
- list_entry(lh->next, struct hpsb_address_serve,
- host_list);
-
- if (as_this->end > as->start)
- break;
-
- if (as_next->start >= as->end) {
- list_add(&as->host_list, lh);
- list_add_tail(&as->hl_list, &hl->addr_list);
- retval = 1;
- break;
- }
- }
- write_unlock_irqrestore(&addr_space_lock, flags);
-
- if (retval == 0)
- kfree(as);
- return retval;
-}
-
-int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- u64 start)
-{
- int retval = 0;
- struct hpsb_address_serve *as;
- struct list_head *lh, *next;
- unsigned long flags;
-
- write_lock_irqsave(&addr_space_lock, flags);
- list_for_each_safe (lh, next, &hl->addr_list) {
- as = list_entry(lh, struct hpsb_address_serve, hl_list);
- if (as->start == start && as->host == host) {
- __delete_addr(as);
- retval = 1;
- break;
- }
- }
- write_unlock_irqrestore(&addr_space_lock, flags);
- return retval;
-}
-
-static const struct hpsb_address_ops dummy_ops;
-
-/* dummy address spaces as lower and upper bounds of the host's a.s. list */
-static void init_hpsb_highlevel(struct hpsb_host *host)
-{
- INIT_LIST_HEAD(&host->dummy_zero_addr.host_list);
- INIT_LIST_HEAD(&host->dummy_zero_addr.hl_list);
- INIT_LIST_HEAD(&host->dummy_max_addr.host_list);
- INIT_LIST_HEAD(&host->dummy_max_addr.hl_list);
-
- host->dummy_zero_addr.op = host->dummy_max_addr.op = &dummy_ops;
-
- host->dummy_zero_addr.start = host->dummy_zero_addr.end = 0;
- host->dummy_max_addr.start = host->dummy_max_addr.end = ((u64) 1) << 48;
-
- list_add_tail(&host->dummy_zero_addr.host_list, &host->addr_space);
- list_add_tail(&host->dummy_max_addr.host_list, &host->addr_space);
-}
-
-void highlevel_add_host(struct hpsb_host *host)
-{
- struct hpsb_highlevel *hl;
-
- init_hpsb_highlevel(host);
-
- down_read(&hl_drivers_sem);
- list_for_each_entry(hl, &hl_drivers, hl_list) {
- if (hl->add_host)
- hl->add_host(host);
- }
- up_read(&hl_drivers_sem);
- if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
- HPSB_ERR("Failed to generate Configuration ROM image for host "
- "%s-%d", hl->name, host->id);
-}
-
-void highlevel_remove_host(struct hpsb_host *host)
-{
- struct hpsb_highlevel *hl;
-
- down_read(&hl_drivers_sem);
- list_for_each_entry(hl, &hl_drivers, hl_list)
- __unregister_host(hl, host, 0);
- up_read(&hl_drivers_sem);
-}
-
-void highlevel_host_reset(struct hpsb_host *host)
-{
- unsigned long flags;
- struct hpsb_highlevel *hl;
-
- read_lock_irqsave(&hl_irqs_lock, flags);
- list_for_each_entry(hl, &hl_irqs, irq_list) {
- if (hl->host_reset)
- hl->host_reset(host);
- }
- read_unlock_irqrestore(&hl_irqs_lock, flags);
-}
-
-void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
- void *data, size_t length)
-{
- unsigned long flags;
- struct hpsb_highlevel *hl;
- int cts = ((quadlet_t *)data)[0] >> 4;
-
- read_lock_irqsave(&hl_irqs_lock, flags);
- list_for_each_entry(hl, &hl_irqs, irq_list) {
- if (hl->fcp_request)
- hl->fcp_request(host, nodeid, direction, cts, data,
- length);
- }
- read_unlock_irqrestore(&hl_irqs_lock, flags);
-}
-
-/*
- * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64:
- *
- * These functions are called to handle transactions. They are called when a
- * packet arrives. The flags argument contains the second word of the first
- * header quadlet of the incoming packet (containing transaction label, retry
- * code, transaction code and priority). These functions either return a
- * response code or a negative number. In the first case a response will be
- * generated. In the latter case, no response will be sent and the driver which
- * handled the request will send the response itself.
- */
-int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
- unsigned int length, u16 flags)
-{
- struct hpsb_address_serve *as;
- unsigned int partlength;
- int rcode = RCODE_ADDRESS_ERROR;
-
- read_lock(&addr_space_lock);
- list_for_each_entry(as, &host->addr_space, host_list) {
- if (as->start > addr)
- break;
-
- if (as->end > addr) {
- partlength = min(as->end - addr, (u64) length);
-
- if (as->op->read)
- rcode = as->op->read(host, nodeid, data,
- addr, partlength, flags);
- else
- rcode = RCODE_TYPE_ERROR;
-
- data += partlength;
- length -= partlength;
- addr += partlength;
-
- if ((rcode != RCODE_COMPLETE) || !length)
- break;
- }
- }
- read_unlock(&addr_space_lock);
-
- if (length && (rcode == RCODE_COMPLETE))
- rcode = RCODE_ADDRESS_ERROR;
- return rcode;
-}
-
-int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
- u64 addr, unsigned int length, u16 flags)
-{
- struct hpsb_address_serve *as;
- unsigned int partlength;
- int rcode = RCODE_ADDRESS_ERROR;
-
- read_lock(&addr_space_lock);
- list_for_each_entry(as, &host->addr_space, host_list) {
- if (as->start > addr)
- break;
-
- if (as->end > addr) {
- partlength = min(as->end - addr, (u64) length);
-
- if (as->op->write)
- rcode = as->op->write(host, nodeid, destid,
- data, addr, partlength,
- flags);
- else
- rcode = RCODE_TYPE_ERROR;
-
- data += partlength;
- length -= partlength;
- addr += partlength;
-
- if ((rcode != RCODE_COMPLETE) || !length)
- break;
- }
- }
- read_unlock(&addr_space_lock);
-
- if (length && (rcode == RCODE_COMPLETE))
- rcode = RCODE_ADDRESS_ERROR;
- return rcode;
-}
-
-int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
- u16 flags)
-{
- struct hpsb_address_serve *as;
- int rcode = RCODE_ADDRESS_ERROR;
-
- read_lock(&addr_space_lock);
- list_for_each_entry(as, &host->addr_space, host_list) {
- if (as->start > addr)
- break;
-
- if (as->end > addr) {
- if (as->op->lock)
- rcode = as->op->lock(host, nodeid, store, addr,
- data, arg, ext_tcode,
- flags);
- else
- rcode = RCODE_TYPE_ERROR;
- break;
- }
- }
- read_unlock(&addr_space_lock);
- return rcode;
-}
-
-int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
- u16 flags)
-{
- struct hpsb_address_serve *as;
- int rcode = RCODE_ADDRESS_ERROR;
-
- read_lock(&addr_space_lock);
-
- list_for_each_entry(as, &host->addr_space, host_list) {
- if (as->start > addr)
- break;
-
- if (as->end > addr) {
- if (as->op->lock64)
- rcode = as->op->lock64(host, nodeid, store,
- addr, data, arg,
- ext_tcode, flags);
- else
- rcode = RCODE_TYPE_ERROR;
- break;
- }
- }
- read_unlock(&addr_space_lock);
- return rcode;
-}
diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h
deleted file mode 100644
index 9dba89fc60ad..000000000000
--- a/drivers/ieee1394/highlevel.h
+++ /dev/null
@@ -1,141 +0,0 @@
-#ifndef IEEE1394_HIGHLEVEL_H
-#define IEEE1394_HIGHLEVEL_H
-
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-
-struct module;
-
-#include "ieee1394_types.h"
-
-struct hpsb_host;
-
-/* internal to ieee1394 core */
-struct hpsb_address_serve {
- struct list_head host_list; /* per host list */
- struct list_head hl_list; /* hpsb_highlevel list */
- const struct hpsb_address_ops *op;
- struct hpsb_host *host;
- u64 start; /* first address handled, quadlet aligned */
- u64 end; /* first address behind, quadlet aligned */
-};
-
-/* Only the following structures are of interest to actual highlevel drivers. */
-
-struct hpsb_highlevel {
- const char *name;
-
- /* Any of the following pointers can legally be NULL. */
-
- /* New host initialized. Will also be called during
- * hpsb_register_highlevel for all hosts already installed. */
- void (*add_host)(struct hpsb_host *host);
-
- /* Host about to be removed. Will also be called during
- * hpsb_unregister_highlevel once for each host. */
- void (*remove_host)(struct hpsb_host *host);
-
- /* Host experienced bus reset with possible configuration changes.
- * Note that this one may occur during interrupt/bottom half handling.
- * You can not expect to be able to do stock hpsb_reads. */
- void (*host_reset)(struct hpsb_host *host);
-
- /* A write request was received on either the FCP_COMMAND (direction =
- * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg
- * contains the cts field (first byte of data). */
- void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction,
- int cts, u8 *data, size_t length);
-
- /* These are initialized by the subsystem when the
- * hpsb_higlevel is registered. */
- struct list_head hl_list;
- struct list_head irq_list;
- struct list_head addr_list;
-
- struct list_head host_info_list;
- rwlock_t host_info_lock;
-};
-
-struct hpsb_address_ops {
- /*
- * Null function pointers will make the respective operation complete
- * with RCODE_TYPE_ERROR. Makes for easy to implement read-only
- * registers (just leave everything but read NULL).
- *
- * All functions shall return appropriate IEEE 1394 rcodes.
- */
-
- /* These functions have to implement block reads for themselves.
- *
- * These functions either return a response code or a negative number.
- * In the first case a response will be generated. In the latter case,
- * no response will be sent and the driver which handled the request
- * will send the response itself. */
- int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, size_t length, u16 flags);
- int (*write)(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, size_t length, u16 flags);
-
- /* Lock transactions: write results of ext_tcode operation into
- * *store. */
- int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
- u16 flags);
- int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
- u16 flags);
-};
-
-void highlevel_add_host(struct hpsb_host *host);
-void highlevel_remove_host(struct hpsb_host *host);
-void highlevel_host_reset(struct hpsb_host *host);
-int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
- unsigned int length, u16 flags);
-int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
- u64 addr, unsigned int length, u16 flags);
-int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
- u16 flags);
-int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
- u16 flags);
-void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
- void *data, size_t length);
-
-/**
- * hpsb_init_highlevel - initialize a struct hpsb_highlevel
- *
- * This is only necessary if hpsb_get_hostinfo_bykey can be called
- * before hpsb_register_highlevel.
- */
-static inline void hpsb_init_highlevel(struct hpsb_highlevel *hl)
-{
- rwlock_init(&hl->host_info_lock);
- INIT_LIST_HEAD(&hl->host_info_list);
-}
-void hpsb_register_highlevel(struct hpsb_highlevel *hl);
-void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
-
-u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
- struct hpsb_host *host,
- const struct hpsb_address_ops *ops,
- u64 size, u64 alignment,
- u64 start, u64 end);
-int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- const struct hpsb_address_ops *ops,
- u64 start, u64 end);
-int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- u64 start);
-
-void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
-void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
- size_t data_size);
-void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
-void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
- unsigned long key);
-void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key);
-int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
- void *data);
-
-#endif /* IEEE1394_HIGHLEVEL_H */
diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c
deleted file mode 100644
index e947d8ffac85..000000000000
--- a/drivers/ieee1394/hosts.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * Low level (host adapter) management.
- *
- * Copyright (C) 1999 Andreas E. Bombe
- * Copyright (C) 1999 Emanuel Pirker
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <linux/mutex.h>
-
-#include "csr1212.h"
-#include "ieee1394.h"
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "nodemgr.h"
-#include "csr.h"
-#include "config_roms.h"
-
-
-static void delayed_reset_bus(struct work_struct *work)
-{
- struct hpsb_host *host =
- container_of(work, struct hpsb_host, delayed_reset.work);
- u8 generation = host->csr.generation + 1;
-
- /* The generation field rolls over to 2 rather than 0 per IEEE
- * 1394a-2000. */
- if (generation > 0xf || generation < 2)
- generation = 2;
-
- csr_set_bus_info_generation(host->csr.rom, generation);
- if (csr1212_generate_csr_image(host->csr.rom) != CSR1212_SUCCESS) {
- /* CSR image creation failed.
- * Reset generation field and do not issue a bus reset. */
- csr_set_bus_info_generation(host->csr.rom,
- host->csr.generation);
- return;
- }
-
- host->csr.generation = generation;
-
- host->update_config_rom = 0;
- if (host->driver->set_hw_config_rom)
- host->driver->set_hw_config_rom(host,
- host->csr.rom->bus_info_data);
-
- host->csr.gen_timestamp[host->csr.generation] = jiffies;
- hpsb_reset_bus(host, SHORT_RESET);
-}
-
-static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p)
-{
- return 0;
-}
-
-static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg)
-{
- return -1;
-}
-
-static int dummy_isoctl(struct hpsb_iso *iso, enum isoctl_cmd command,
- unsigned long arg)
-{
- return -1;
-}
-
-static struct hpsb_host_driver dummy_driver = {
- .transmit_packet = dummy_transmit_packet,
- .devctl = dummy_devctl,
- .isoctl = dummy_isoctl
-};
-
-static int alloc_hostnum_cb(struct hpsb_host *host, void *__data)
-{
- int *hostnum = __data;
-
- if (host->id == *hostnum)
- return 1;
-
- return 0;
-}
-
-static DEFINE_MUTEX(host_num_alloc);
-
-/**
- * hpsb_alloc_host - allocate a new host controller.
- * @drv: the driver that will manage the host controller
- * @extra: number of extra bytes to allocate for the driver
- *
- * Allocate a &hpsb_host and initialize the general subsystem specific
- * fields. If the driver needs to store per host data, as drivers
- * usually do, the amount of memory required can be specified by the
- * @extra parameter. Once allocated, the driver should initialize the
- * driver specific parts, enable the controller and make it available
- * to the general subsystem using hpsb_add_host().
- *
- * Return Value: a pointer to the &hpsb_host if successful, %NULL if
- * no memory was available.
- */
-struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
- struct device *dev)
-{
- struct hpsb_host *h;
- int i;
- int hostnum = 0;
-
- h = kzalloc(sizeof(*h) + extra, GFP_KERNEL);
- if (!h)
- return NULL;
-
- h->csr.rom = csr1212_create_csr(&csr_bus_ops, CSR_BUS_INFO_SIZE, h);
- if (!h->csr.rom)
- goto fail;
-
- h->hostdata = h + 1;
- h->driver = drv;
-
- INIT_LIST_HEAD(&h->pending_packets);
- INIT_LIST_HEAD(&h->addr_space);
-
- for (i = 2; i < 16; i++)
- h->csr.gen_timestamp[i] = jiffies - 60 * HZ;
-
- atomic_set(&h->generation, 0);
-
- INIT_DELAYED_WORK(&h->delayed_reset, delayed_reset_bus);
-
- init_timer(&h->timeout);
- h->timeout.data = (unsigned long) h;
- h->timeout.function = abort_timedouts;
- h->timeout_interval = HZ / 20; /* 50ms, half of minimum SPLIT_TIMEOUT */
-
- h->topology_map = h->csr.topology_map + 3;
- h->speed_map = (u8 *)(h->csr.speed_map + 2);
-
- mutex_lock(&host_num_alloc);
- while (nodemgr_for_each_host(&hostnum, alloc_hostnum_cb))
- hostnum++;
- mutex_unlock(&host_num_alloc);
- h->id = hostnum;
-
- memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device));
- h->device.parent = dev;
- set_dev_node(&h->device, dev_to_node(dev));
- dev_set_name(&h->device, "fw-host%d", h->id);
-
- h->host_dev.parent = &h->device;
- h->host_dev.class = &hpsb_host_class;
- dev_set_name(&h->host_dev, "fw-host%d", h->id);
-
- if (device_register(&h->device))
- goto fail;
- if (device_register(&h->host_dev)) {
- device_unregister(&h->device);
- goto fail;
- }
- get_device(&h->device);
-
- return h;
-
-fail:
- kfree(h);
- return NULL;
-}
-
-int hpsb_add_host(struct hpsb_host *host)
-{
- if (hpsb_default_host_entry(host))
- return -ENOMEM;
-
- highlevel_add_host(host);
- return 0;
-}
-
-void hpsb_resume_host(struct hpsb_host *host)
-{
- if (host->driver->set_hw_config_rom)
- host->driver->set_hw_config_rom(host,
- host->csr.rom->bus_info_data);
- host->driver->devctl(host, RESET_BUS, SHORT_RESET);
-}
-
-void hpsb_remove_host(struct hpsb_host *host)
-{
- host->is_shutdown = 1;
-
- cancel_delayed_work(&host->delayed_reset);
- flush_scheduled_work();
-
- host->driver = &dummy_driver;
- highlevel_remove_host(host);
-
- device_unregister(&host->host_dev);
- device_unregister(&host->device);
-}
-
-/**
- * hpsb_update_config_rom_image - updates configuration ROM image of a host
- *
- * Updates the configuration ROM image of a host. rom_version must be the
- * current version, otherwise it will fail with return value -1. If this
- * host does not support config-rom-update, it will return -%EINVAL.
- * Return value 0 indicates success.
- */
-int hpsb_update_config_rom_image(struct hpsb_host *host)
-{
- unsigned long reset_delay;
- int next_gen = host->csr.generation + 1;
-
- if (!host->update_config_rom)
- return -EINVAL;
-
- if (next_gen > 0xf)
- next_gen = 2;
-
- /* Stop the delayed interrupt, we're about to change the config rom and
- * it would be a waste to do a bus reset twice. */
- cancel_delayed_work(&host->delayed_reset);
-
- /* IEEE 1394a-2000 prohibits using the same generation number
- * twice in a 60 second period. */
- if (time_before(jiffies, host->csr.gen_timestamp[next_gen] + 60 * HZ))
- /* Wait 60 seconds from the last time this generation number was
- * used. */
- reset_delay =
- (60 * HZ) + host->csr.gen_timestamp[next_gen] - jiffies;
- else
- /* Wait 1 second in case some other code wants to change the
- * Config ROM in the near future. */
- reset_delay = HZ;
-
- PREPARE_DELAYED_WORK(&host->delayed_reset, delayed_reset_bus);
- schedule_delayed_work(&host->delayed_reset, reset_delay);
-
- return 0;
-}
diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h
deleted file mode 100644
index 49c359022c54..000000000000
--- a/drivers/ieee1394/hosts.h
+++ /dev/null
@@ -1,201 +0,0 @@
-#ifndef _IEEE1394_HOSTS_H
-#define _IEEE1394_HOSTS_H
-
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/timer.h>
-#include <linux/types.h>
-#include <linux/workqueue.h>
-#include <asm/atomic.h>
-
-struct pci_dev;
-struct module;
-
-#include "ieee1394_types.h"
-#include "csr.h"
-#include "highlevel.h"
-
-struct hpsb_packet;
-struct hpsb_iso;
-
-struct hpsb_host {
- struct list_head host_list;
-
- void *hostdata;
-
- atomic_t generation;
-
- struct list_head pending_packets;
- struct timer_list timeout;
- unsigned long timeout_interval;
-
- int node_count; /* number of identified nodes on this bus */
- int selfid_count; /* total number of SelfIDs received */
- int nodes_active; /* number of nodes with active link layer */
-
- nodeid_t node_id; /* node ID of this host */
- nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
- nodeid_t busmgr_id; /* ID of this bus' bus manager */
-
- /* this nodes state */
- unsigned in_bus_reset:1;
- unsigned is_shutdown:1;
- unsigned resume_packet_sent:1;
-
- /* this nodes' duties on the bus */
- unsigned is_root:1;
- unsigned is_cycmst:1;
- unsigned is_irm:1;
- unsigned is_busmgr:1;
-
- int reset_retries;
- quadlet_t *topology_map;
- u8 *speed_map;
-
- int id;
- struct hpsb_host_driver *driver;
- struct pci_dev *pdev;
- struct device device;
- struct device host_dev;
-
- struct delayed_work delayed_reset;
- unsigned config_roms:31;
- unsigned update_config_rom:1;
-
- struct list_head addr_space;
- u64 low_addr_space; /* upper bound of physical DMA area */
- u64 middle_addr_space; /* upper bound of posted write area */
-
- u8 speed[ALL_NODES]; /* speed between each node and local node */
-
- /* per node tlabel allocation */
- u8 next_tl[ALL_NODES];
- struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES];
-
- struct csr_control csr;
-
- struct hpsb_address_serve dummy_zero_addr;
- struct hpsb_address_serve dummy_max_addr;
-};
-
-enum devctl_cmd {
- /* Host is requested to reset its bus and cancel all outstanding async
- * requests. If arg == 1, it shall also attempt to become root on the
- * bus. Return void. */
- RESET_BUS,
-
- /* Arg is void, return value is the hardware cycle counter value. */
- GET_CYCLE_COUNTER,
-
- /* Set the hardware cycle counter to the value in arg, return void.
- * FIXME - setting is probably not required. */
- SET_CYCLE_COUNTER,
-
- /* Configure hardware for new bus ID in arg, return void. */
- SET_BUS_ID,
-
- /* If arg true, start sending cycle start packets, stop if arg == 0.
- * Return void. */
- ACT_CYCLE_MASTER,
-
- /* Cancel all outstanding async requests without resetting the bus.
- * Return void. */
- CANCEL_REQUESTS,
-};
-
-enum isoctl_cmd {
- /* rawiso API - see iso.h for the meanings of these commands
- * (they correspond exactly to the hpsb_iso_* API functions)
- * INIT = allocate resources
- * START = begin transmission/reception
- * STOP = halt transmission/reception
- * QUEUE/RELEASE = produce/consume packets
- * SHUTDOWN = deallocate resources
- */
-
- XMIT_INIT,
- XMIT_START,
- XMIT_STOP,
- XMIT_QUEUE,
- XMIT_SHUTDOWN,
-
- RECV_INIT,
- RECV_LISTEN_CHANNEL, /* multi-channel only */
- RECV_UNLISTEN_CHANNEL, /* multi-channel only */
- RECV_SET_CHANNEL_MASK, /* multi-channel only; arg is a *u64 */
- RECV_START,
- RECV_STOP,
- RECV_RELEASE,
- RECV_SHUTDOWN,
- RECV_FLUSH
-};
-
-enum reset_types {
- /* 166 microsecond reset -- only type of reset available on
- non-1394a capable controllers */
- LONG_RESET,
-
- /* Short (arbitrated) reset -- only available on 1394a capable
- controllers */
- SHORT_RESET,
-
- /* Variants that set force_root before issueing the bus reset */
- LONG_RESET_FORCE_ROOT, SHORT_RESET_FORCE_ROOT,
-
- /* Variants that clear force_root before issueing the bus reset */
- LONG_RESET_NO_FORCE_ROOT, SHORT_RESET_NO_FORCE_ROOT
-};
-
-struct hpsb_host_driver {
- struct module *owner;
- const char *name;
-
- /* The hardware driver may optionally support a function that is used
- * to set the hardware ConfigROM if the hardware supports handling
- * reads to the ConfigROM on its own. */
- void (*set_hw_config_rom)(struct hpsb_host *host,
- __be32 *config_rom);
-
- /* This function shall implement packet transmission based on
- * packet->type. It shall CRC both parts of the packet (unless
- * packet->type == raw) and do byte-swapping as necessary or instruct
- * the hardware to do so. It can return immediately after the packet
- * was queued for sending. After sending, hpsb_sent_packet() has to be
- * called. Return 0 on success, negative errno on failure.
- * NOTE: The function must be callable in interrupt context.
- */
- int (*transmit_packet)(struct hpsb_host *host,
- struct hpsb_packet *packet);
-
- /* This function requests miscellanous services from the driver, see
- * above for command codes and expected actions. Return -1 for unknown
- * command, though that should never happen.
- */
- int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg);
-
- /* ISO transmission/reception functions. Return 0 on success, -1
- * (or -EXXX errno code) on failure. If the low-level driver does not
- * support the new ISO API, set isoctl to NULL.
- */
- int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command,
- unsigned long arg);
-
- /* This function is mainly to redirect local CSR reads/locks to the iso
- * management registers (bus manager id, bandwidth available, channels
- * available) to the hardware registers in OHCI. reg is 0,1,2,3 for bus
- * mgr, bwdth avail, ch avail hi, ch avail lo respectively (the same ids
- * as OHCI uses). data and compare are the new data and expected data
- * respectively, return value is the old value.
- */
- quadlet_t (*hw_csr_reg) (struct hpsb_host *host, int reg,
- quadlet_t data, quadlet_t compare);
-};
-
-struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
- struct device *dev);
-int hpsb_add_host(struct hpsb_host *host);
-void hpsb_resume_host(struct hpsb_host *host);
-void hpsb_remove_host(struct hpsb_host *host);
-int hpsb_update_config_rom_image(struct hpsb_host *host);
-
-#endif /* _IEEE1394_HOSTS_H */
diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h
deleted file mode 100644
index 46878fef136c..000000000000
--- a/drivers/ieee1394/ieee1394-ioctl.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Base file for all ieee1394 ioctl's.
- * Linux-1394 has allocated base '#' with a range of 0x00-0x3f.
- */
-
-#ifndef __IEEE1394_IOCTL_H
-#define __IEEE1394_IOCTL_H
-
-#include <linux/ioctl.h>
-#include <linux/types.h>
-
-/* DV1394 Gets 10 */
-
-/* Get the driver ready to transmit video. pass a struct dv1394_init* as
- * the parameter (see below), or NULL to get default parameters */
-#define DV1394_IOC_INIT _IOW('#', 0x06, struct dv1394_init)
-
-/* Stop transmitting video and free the ringbuffer */
-#define DV1394_IOC_SHUTDOWN _IO ('#', 0x07)
-
-/* Submit N new frames to be transmitted, where the index of the first new
- * frame is first_clear_buffer, and the index of the last new frame is
- * (first_clear_buffer + N) % n_frames */
-#define DV1394_IOC_SUBMIT_FRAMES _IO ('#', 0x08)
-
-/* Block until N buffers are clear (pass N as the parameter) Because we
- * re-transmit the last frame on underrun, there will at most be n_frames
- * - 1 clear frames at any time */
-#define DV1394_IOC_WAIT_FRAMES _IO ('#', 0x09)
-
-/* Capture new frames that have been received, where the index of the
- * first new frame is first_clear_buffer, and the index of the last new
- * frame is (first_clear_buffer + N) % n_frames */
-#define DV1394_IOC_RECEIVE_FRAMES _IO ('#', 0x0a)
-
-/* Tell card to start receiving DMA */
-#define DV1394_IOC_START_RECEIVE _IO ('#', 0x0b)
-
-/* Pass a struct dv1394_status* as the parameter */
-#define DV1394_IOC_GET_STATUS _IOR('#', 0x0c, struct dv1394_status)
-
-
-/* Video1394 Gets 10 */
-
-#define VIDEO1394_IOC_LISTEN_CHANNEL \
- _IOWR('#', 0x10, struct video1394_mmap)
-#define VIDEO1394_IOC_UNLISTEN_CHANNEL \
- _IOW ('#', 0x11, int)
-#define VIDEO1394_IOC_LISTEN_QUEUE_BUFFER \
- _IOW ('#', 0x12, struct video1394_wait)
-#define VIDEO1394_IOC_LISTEN_WAIT_BUFFER \
- _IOWR('#', 0x13, struct video1394_wait)
-#define VIDEO1394_IOC_TALK_CHANNEL \
- _IOWR('#', 0x14, struct video1394_mmap)
-#define VIDEO1394_IOC_UNTALK_CHANNEL \
- _IOW ('#', 0x15, int)
-/*
- * This one is broken: it really wanted
- * "sizeof (struct video1394_wait) + sizeof (struct video1394_queue_variable)"
- * but got just a "size_t"
- */
-#define VIDEO1394_IOC_TALK_QUEUE_BUFFER \
- _IOW ('#', 0x16, size_t)
-#define VIDEO1394_IOC_TALK_WAIT_BUFFER \
- _IOW ('#', 0x17, struct video1394_wait)
-#define VIDEO1394_IOC_LISTEN_POLL_BUFFER \
- _IOWR('#', 0x18, struct video1394_wait)
-
-
-/* Raw1394's ISO interface */
-#define RAW1394_IOC_ISO_XMIT_INIT \
- _IOW ('#', 0x1a, struct raw1394_iso_status)
-#define RAW1394_IOC_ISO_RECV_INIT \
- _IOWR('#', 0x1b, struct raw1394_iso_status)
-#define RAW1394_IOC_ISO_RECV_START \
- _IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3)
-#define RAW1394_IOC_ISO_XMIT_START \
- _IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2)
-#define RAW1394_IOC_ISO_XMIT_RECV_STOP \
- _IO ('#', 0x1e)
-#define RAW1394_IOC_ISO_GET_STATUS \
- _IOR ('#', 0x1f, struct raw1394_iso_status)
-#define RAW1394_IOC_ISO_SHUTDOWN \
- _IO ('#', 0x20)
-#define RAW1394_IOC_ISO_QUEUE_ACTIVITY \
- _IO ('#', 0x21)
-#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL \
- _IOW ('#', 0x22, unsigned char)
-#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL \
- _IOW ('#', 0x23, unsigned char)
-#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK \
- _IOW ('#', 0x24, __u64)
-#define RAW1394_IOC_ISO_RECV_PACKETS \
- _IOW ('#', 0x25, struct raw1394_iso_packets)
-#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS \
- _IOW ('#', 0x26, unsigned int)
-#define RAW1394_IOC_ISO_XMIT_PACKETS \
- _IOW ('#', 0x27, struct raw1394_iso_packets)
-#define RAW1394_IOC_ISO_XMIT_SYNC \
- _IO ('#', 0x28)
-#define RAW1394_IOC_ISO_RECV_FLUSH \
- _IO ('#', 0x29)
-#define RAW1394_IOC_GET_CYCLE_TIMER \
- _IOR ('#', 0x30, struct raw1394_cycle_timer)
-
-#endif /* __IEEE1394_IOCTL_H */
diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h
deleted file mode 100644
index af320e2c5079..000000000000
--- a/drivers/ieee1394/ieee1394.h
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Generic IEEE 1394 definitions
- */
-
-#ifndef _IEEE1394_IEEE1394_H
-#define _IEEE1394_IEEE1394_H
-
-#define TCODE_WRITEQ 0x0
-#define TCODE_WRITEB 0x1
-#define TCODE_WRITE_RESPONSE 0x2
-#define TCODE_READQ 0x4
-#define TCODE_READB 0x5
-#define TCODE_READQ_RESPONSE 0x6
-#define TCODE_READB_RESPONSE 0x7
-#define TCODE_CYCLE_START 0x8
-#define TCODE_LOCK_REQUEST 0x9
-#define TCODE_ISO_DATA 0xa
-#define TCODE_STREAM_DATA 0xa
-#define TCODE_LOCK_RESPONSE 0xb
-
-#define RCODE_COMPLETE 0x0
-#define RCODE_CONFLICT_ERROR 0x4
-#define RCODE_DATA_ERROR 0x5
-#define RCODE_TYPE_ERROR 0x6
-#define RCODE_ADDRESS_ERROR 0x7
-
-#define EXTCODE_MASK_SWAP 0x1
-#define EXTCODE_COMPARE_SWAP 0x2
-#define EXTCODE_FETCH_ADD 0x3
-#define EXTCODE_LITTLE_ADD 0x4
-#define EXTCODE_BOUNDED_ADD 0x5
-#define EXTCODE_WRAP_ADD 0x6
-
-#define ACK_COMPLETE 0x1
-#define ACK_PENDING 0x2
-#define ACK_BUSY_X 0x4
-#define ACK_BUSY_A 0x5
-#define ACK_BUSY_B 0x6
-#define ACK_TARDY 0xb
-#define ACK_CONFLICT_ERROR 0xc
-#define ACK_DATA_ERROR 0xd
-#define ACK_TYPE_ERROR 0xe
-#define ACK_ADDRESS_ERROR 0xf
-
-/* Non-standard "ACK codes" for internal use */
-#define ACKX_NONE (-1)
-#define ACKX_SEND_ERROR (-2)
-#define ACKX_ABORTED (-3)
-#define ACKX_TIMEOUT (-4)
-
-#define IEEE1394_SPEED_100 0x00
-#define IEEE1394_SPEED_200 0x01
-#define IEEE1394_SPEED_400 0x02
-#define IEEE1394_SPEED_800 0x03
-#define IEEE1394_SPEED_1600 0x04
-#define IEEE1394_SPEED_3200 0x05
-#define IEEE1394_SPEED_MAX IEEE1394_SPEED_3200
-
-/* Maps speed values above to a string representation */
-extern const char *hpsb_speedto_str[];
-
-/* 1394a cable PHY packets */
-#define SELFID_PWRCL_NO_POWER 0x0
-#define SELFID_PWRCL_PROVIDE_15W 0x1
-#define SELFID_PWRCL_PROVIDE_30W 0x2
-#define SELFID_PWRCL_PROVIDE_45W 0x3
-#define SELFID_PWRCL_USE_1W 0x4
-#define SELFID_PWRCL_USE_3W 0x5
-#define SELFID_PWRCL_USE_6W 0x6
-#define SELFID_PWRCL_USE_10W 0x7
-
-#define SELFID_PORT_CHILD 0x3
-#define SELFID_PORT_PARENT 0x2
-#define SELFID_PORT_NCONN 0x1
-#define SELFID_PORT_NONE 0x0
-
-#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */
-
-#define PHYPACKET_LINKON 0x40000000
-#define PHYPACKET_PHYCONFIG_R 0x00800000
-#define PHYPACKET_PHYCONFIG_T 0x00400000
-#define EXTPHYPACKET_TYPE_PING 0x00000000
-#define EXTPHYPACKET_TYPE_REMOTEACCESS_BASE 0x00040000
-#define EXTPHYPACKET_TYPE_REMOTEACCESS_PAGED 0x00140000
-#define EXTPHYPACKET_TYPE_REMOTEREPLY_BASE 0x000C0000
-#define EXTPHYPACKET_TYPE_REMOTEREPLY_PAGED 0x001C0000
-#define EXTPHYPACKET_TYPE_REMOTECOMMAND 0x00200000
-#define EXTPHYPACKET_TYPE_REMOTECONFIRMATION 0x00280000
-#define EXTPHYPACKET_TYPE_RESUME 0x003C0000
-
-#define EXTPHYPACKET_TYPEMASK 0xC0FC0000
-
-#define PHYPACKET_PORT_SHIFT 24
-#define PHYPACKET_GAPCOUNT_SHIFT 16
-
-/* 1394a PHY register map bitmasks */
-#define PHY_00_PHYSICAL_ID 0xFC
-#define PHY_00_R 0x02 /* Root */
-#define PHY_00_PS 0x01 /* Power Status*/
-#define PHY_01_RHB 0x80 /* Root Hold-Off */
-#define PHY_01_IBR 0x80 /* Initiate Bus Reset */
-#define PHY_01_GAP_COUNT 0x3F
-#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */
-#define PHY_02_TOTAL_PORTS 0x1F
-#define PHY_03_MAX_SPEED 0xE0
-#define PHY_03_DELAY 0x0F
-#define PHY_04_LCTRL 0x80 /* Link Active Report Control */
-#define PHY_04_CONTENDER 0x40
-#define PHY_04_JITTER 0x38
-#define PHY_04_PWR_CLASS 0x07 /* Power Class */
-#define PHY_05_WATCHDOG 0x80
-#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */
-#define PHY_05_LOOP 0x20 /* Loop Detect */
-#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */
-#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */
-#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */
-#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */
-#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */
-
-#include <asm/byteorder.h>
-
-/* '1' '3' '9' '4' in ASCII */
-#define IEEE1394_BUSID_MAGIC cpu_to_be32(0x31333934)
-
-#ifdef __BIG_ENDIAN_BITFIELD
-
-struct selfid {
- u32 packet_identifier:2; /* always binary 10 */
- u32 phy_id:6;
- /* byte */
- u32 extended:1; /* if true is struct ext_selfid */
- u32 link_active:1;
- u32 gap_count:6;
- /* byte */
- u32 speed:2;
- u32 phy_delay:2;
- u32 contender:1;
- u32 power_class:3;
- /* byte */
- u32 port0:2;
- u32 port1:2;
- u32 port2:2;
- u32 initiated_reset:1;
- u32 more_packets:1;
-} __attribute__((packed));
-
-struct ext_selfid {
- u32 packet_identifier:2; /* always binary 10 */
- u32 phy_id:6;
- /* byte */
- u32 extended:1; /* if false is struct selfid */
- u32 seq_nr:3;
- u32 reserved:2;
- u32 porta:2;
- /* byte */
- u32 portb:2;
- u32 portc:2;
- u32 portd:2;
- u32 porte:2;
- /* byte */
- u32 portf:2;
- u32 portg:2;
- u32 porth:2;
- u32 reserved2:1;
- u32 more_packets:1;
-} __attribute__((packed));
-
-#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */
-
-/*
- * Note: these mean to be bit fields of a big endian SelfID as seen on a little
- * endian machine. Without swapping.
- */
-
-struct selfid {
- u32 phy_id:6;
- u32 packet_identifier:2; /* always binary 10 */
- /* byte */
- u32 gap_count:6;
- u32 link_active:1;
- u32 extended:1; /* if true is struct ext_selfid */
- /* byte */
- u32 power_class:3;
- u32 contender:1;
- u32 phy_delay:2;
- u32 speed:2;
- /* byte */
- u32 more_packets:1;
- u32 initiated_reset:1;
- u32 port2:2;
- u32 port1:2;
- u32 port0:2;
-} __attribute__((packed));
-
-struct ext_selfid {
- u32 phy_id:6;
- u32 packet_identifier:2; /* always binary 10 */
- /* byte */
- u32 porta:2;
- u32 reserved:2;
- u32 seq_nr:3;
- u32 extended:1; /* if false is struct selfid */
- /* byte */
- u32 porte:2;
- u32 portd:2;
- u32 portc:2;
- u32 portb:2;
- /* byte */
- u32 more_packets:1;
- u32 reserved2:1;
- u32 porth:2;
- u32 portg:2;
- u32 portf:2;
-} __attribute__((packed));
-
-#else
-#error What? PDP endian?
-#endif /* __BIG_ENDIAN_BITFIELD */
-
-#endif /* _IEEE1394_IEEE1394_H */
diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c
deleted file mode 100644
index 872338003721..000000000000
--- a/drivers/ieee1394/ieee1394_core.c
+++ /dev/null
@@ -1,1380 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * Core support: hpsb_packet management, packet handling and forwarding to
- * highlevel or lowlevel code
- *
- * Copyright (C) 1999, 2000 Andreas E. Bombe
- * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- *
- *
- * Contributions:
- *
- * Manfred Weihs <weihs@ict.tuwien.ac.at>
- * loopback functionality in hpsb_send_packet
- * allow highlevel drivers to disable automatic response generation
- * and to generate responses themselves (deferred)
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/bitops.h>
-#include <linux/kdev_t.h>
-#include <linux/freezer.h>
-#include <linux/suspend.h>
-#include <linux/kthread.h>
-#include <linux/preempt.h>
-#include <linux/time.h>
-
-#include <asm/system.h>
-#include <asm/byteorder.h>
-
-#include "ieee1394_types.h"
-#include "ieee1394.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "ieee1394_transactions.h"
-#include "csr.h"
-#include "nodemgr.h"
-#include "dma.h"
-#include "iso.h"
-#include "config_roms.h"
-
-/*
- * Disable the nodemgr detection and config rom reading functionality.
- */
-static int disable_nodemgr;
-module_param(disable_nodemgr, int, 0444);
-MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality.");
-
-/* Disable Isochronous Resource Manager functionality */
-int hpsb_disable_irm = 0;
-module_param_named(disable_irm, hpsb_disable_irm, bool, 0444);
-MODULE_PARM_DESC(disable_irm,
- "Disable Isochronous Resource Manager functionality.");
-
-/* We are GPL, so treat us special */
-MODULE_LICENSE("GPL");
-
-/* Some globals used */
-const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" };
-struct class *hpsb_protocol_class;
-
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-static void dump_packet(const char *text, quadlet_t *data, int size, int speed)
-{
- int i;
-
- size /= 4;
- size = (size > 4 ? 4 : size);
-
- printk(KERN_DEBUG "ieee1394: %s", text);
- if (speed > -1 && speed < 6)
- printk(" at %s", hpsb_speedto_str[speed]);
- printk(":");
- for (i = 0; i < size; i++)
- printk(" %08x", data[i]);
- printk("\n");
-}
-#else
-#define dump_packet(a,b,c,d) do {} while (0)
-#endif
-
-static void abort_requests(struct hpsb_host *host);
-static void queue_packet_complete(struct hpsb_packet *packet);
-
-
-/**
- * hpsb_set_packet_complete_task - set task that runs when a packet completes
- * @packet: the packet whose completion we want the task added to
- * @routine: function to call
- * @data: data (if any) to pass to the above function
- *
- * Set the task that runs when a packet completes. You cannot call this more
- * than once on a single packet before it is sent.
- *
- * Typically, the complete @routine is responsible to call hpsb_free_packet().
- */
-void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
- void (*routine)(void *), void *data)
-{
- WARN_ON(packet->complete_routine != NULL);
- packet->complete_routine = routine;
- packet->complete_data = data;
- return;
-}
-
-/**
- * hpsb_alloc_packet - allocate new packet structure
- * @data_size: size of the data block to be allocated, in bytes
- *
- * This function allocates, initializes and returns a new &struct hpsb_packet.
- * It can be used in interrupt context. A header block is always included and
- * initialized with zeros. Its size is big enough to contain all possible 1394
- * headers. The data block is only allocated if @data_size is not zero.
- *
- * For packets for which responses will be received the @data_size has to be big
- * enough to contain the response's data block since no further allocation
- * occurs at response matching time.
- *
- * The packet's generation value will be set to the current generation number
- * for ease of use. Remember to overwrite it with your own recorded generation
- * number if you can not be sure that your code will not race with a bus reset.
- *
- * Return value: A pointer to a &struct hpsb_packet or NULL on allocation
- * failure.
- */
-struct hpsb_packet *hpsb_alloc_packet(size_t data_size)
-{
- struct hpsb_packet *packet;
-
- data_size = ((data_size + 3) & ~3);
-
- packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC);
- if (!packet)
- return NULL;
-
- packet->state = hpsb_unused;
- packet->generation = -1;
- INIT_LIST_HEAD(&packet->driver_list);
- INIT_LIST_HEAD(&packet->queue);
- atomic_set(&packet->refcnt, 1);
-
- if (data_size) {
- packet->data = packet->embedded_data;
- packet->allocated_data_size = data_size;
- }
- return packet;
-}
-
-/**
- * hpsb_free_packet - free packet and data associated with it
- * @packet: packet to free (is NULL safe)
- *
- * Frees @packet->data only if it was allocated through hpsb_alloc_packet().
- */
-void hpsb_free_packet(struct hpsb_packet *packet)
-{
- if (packet && atomic_dec_and_test(&packet->refcnt)) {
- BUG_ON(!list_empty(&packet->driver_list) ||
- !list_empty(&packet->queue));
- kfree(packet);
- }
-}
-
-/**
- * hpsb_reset_bus - initiate bus reset on the given host
- * @host: host controller whose bus to reset
- * @type: one of enum reset_types
- *
- * Returns 1 if bus reset already in progress, 0 otherwise.
- */
-int hpsb_reset_bus(struct hpsb_host *host, int type)
-{
- if (!host->in_bus_reset) {
- host->driver->devctl(host, RESET_BUS, type);
- return 0;
- } else {
- return 1;
- }
-}
-
-/**
- * hpsb_read_cycle_timer - read cycle timer register and system time
- * @host: host whose isochronous cycle timer register is read
- * @cycle_timer: address of bitfield to return the register contents
- * @local_time: address to return the system time
- *
- * The format of * @cycle_timer, is described in OHCI 1.1 clause 5.13. This
- * format is also read from non-OHCI controllers. * @local_time contains the
- * system time in microseconds since the Epoch, read at the moment when the
- * cycle timer was read.
- *
- * Return value: 0 for success or error number otherwise.
- */
-int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer,
- u64 *local_time)
-{
- int ctr;
- struct timeval tv;
- unsigned long flags;
-
- if (!host || !cycle_timer || !local_time)
- return -EINVAL;
-
- preempt_disable();
- local_irq_save(flags);
-
- ctr = host->driver->devctl(host, GET_CYCLE_COUNTER, 0);
- if (ctr)
- do_gettimeofday(&tv);
-
- local_irq_restore(flags);
- preempt_enable();
-
- if (!ctr)
- return -EIO;
- *cycle_timer = ctr;
- *local_time = tv.tv_sec * 1000000ULL + tv.tv_usec;
- return 0;
-}
-
-/**
- * hpsb_bus_reset - notify a bus reset to the core
- *
- * For host driver module usage. Safe to use in interrupt context, although
- * quite complex; so you may want to run it in the bottom rather than top half.
- *
- * Returns 1 if bus reset already in progress, 0 otherwise.
- */
-int hpsb_bus_reset(struct hpsb_host *host)
-{
- if (host->in_bus_reset) {
- HPSB_NOTICE("%s called while bus reset already in progress",
- __func__);
- return 1;
- }
-
- abort_requests(host);
- host->in_bus_reset = 1;
- host->irm_id = -1;
- host->is_irm = 0;
- host->busmgr_id = -1;
- host->is_busmgr = 0;
- host->is_cycmst = 0;
- host->node_count = 0;
- host->selfid_count = 0;
-
- return 0;
-}
-
-
-/*
- * Verify num_of_selfids SelfIDs and return number of nodes. Return zero in
- * case verification failed.
- */
-static int check_selfids(struct hpsb_host *host)
-{
- int nodeid = -1;
- int rest_of_selfids = host->selfid_count;
- struct selfid *sid = (struct selfid *)host->topology_map;
- struct ext_selfid *esid;
- int esid_seq = 23;
-
- host->nodes_active = 0;
-
- while (rest_of_selfids--) {
- if (!sid->extended) {
- nodeid++;
- esid_seq = 0;
-
- if (sid->phy_id != nodeid) {
- HPSB_INFO("SelfIDs failed monotony check with "
- "%d", sid->phy_id);
- return 0;
- }
-
- if (sid->link_active) {
- host->nodes_active++;
- if (sid->contender)
- host->irm_id = LOCAL_BUS | sid->phy_id;
- }
- } else {
- esid = (struct ext_selfid *)sid;
-
- if ((esid->phy_id != nodeid)
- || (esid->seq_nr != esid_seq)) {
- HPSB_INFO("SelfIDs failed monotony check with "
- "%d/%d", esid->phy_id, esid->seq_nr);
- return 0;
- }
- esid_seq++;
- }
- sid++;
- }
-
- esid = (struct ext_selfid *)(sid - 1);
- while (esid->extended) {
- if ((esid->porta == SELFID_PORT_PARENT) ||
- (esid->portb == SELFID_PORT_PARENT) ||
- (esid->portc == SELFID_PORT_PARENT) ||
- (esid->portd == SELFID_PORT_PARENT) ||
- (esid->porte == SELFID_PORT_PARENT) ||
- (esid->portf == SELFID_PORT_PARENT) ||
- (esid->portg == SELFID_PORT_PARENT) ||
- (esid->porth == SELFID_PORT_PARENT)) {
- HPSB_INFO("SelfIDs failed root check on "
- "extended SelfID");
- return 0;
- }
- esid--;
- }
-
- sid = (struct selfid *)esid;
- if ((sid->port0 == SELFID_PORT_PARENT) ||
- (sid->port1 == SELFID_PORT_PARENT) ||
- (sid->port2 == SELFID_PORT_PARENT)) {
- HPSB_INFO("SelfIDs failed root check");
- return 0;
- }
-
- host->node_count = nodeid + 1;
- return 1;
-}
-
-static void build_speed_map(struct hpsb_host *host, int nodecount)
-{
- u8 cldcnt[nodecount];
- u8 *map = host->speed_map;
- u8 *speedcap = host->speed;
- u8 local_link_speed = host->csr.lnk_spd;
- struct selfid *sid;
- struct ext_selfid *esid;
- int i, j, n;
-
- for (i = 0; i < (nodecount * 64); i += 64) {
- for (j = 0; j < nodecount; j++) {
- map[i+j] = IEEE1394_SPEED_MAX;
- }
- }
-
- for (i = 0; i < nodecount; i++) {
- cldcnt[i] = 0;
- }
-
- /* find direct children count and speed */
- for (sid = (struct selfid *)&host->topology_map[host->selfid_count-1],
- n = nodecount - 1;
- (void *)sid >= (void *)host->topology_map; sid--) {
- if (sid->extended) {
- esid = (struct ext_selfid *)sid;
-
- if (esid->porta == SELFID_PORT_CHILD) cldcnt[n]++;
- if (esid->portb == SELFID_PORT_CHILD) cldcnt[n]++;
- if (esid->portc == SELFID_PORT_CHILD) cldcnt[n]++;
- if (esid->portd == SELFID_PORT_CHILD) cldcnt[n]++;
- if (esid->porte == SELFID_PORT_CHILD) cldcnt[n]++;
- if (esid->portf == SELFID_PORT_CHILD) cldcnt[n]++;
- if (esid->portg == SELFID_PORT_CHILD) cldcnt[n]++;
- if (esid->porth == SELFID_PORT_CHILD) cldcnt[n]++;
- } else {
- if (sid->port0 == SELFID_PORT_CHILD) cldcnt[n]++;
- if (sid->port1 == SELFID_PORT_CHILD) cldcnt[n]++;
- if (sid->port2 == SELFID_PORT_CHILD) cldcnt[n]++;
-
- speedcap[n] = sid->speed;
- if (speedcap[n] > local_link_speed)
- speedcap[n] = local_link_speed;
- n--;
- }
- }
-
- /* set self mapping */
- for (i = 0; i < nodecount; i++) {
- map[64*i + i] = speedcap[i];
- }
-
- /* fix up direct children count to total children count;
- * also fix up speedcaps for sibling and parent communication */
- for (i = 1; i < nodecount; i++) {
- for (j = cldcnt[i], n = i - 1; j > 0; j--) {
- cldcnt[i] += cldcnt[n];
- speedcap[n] = min(speedcap[n], speedcap[i]);
- n -= cldcnt[n] + 1;
- }
- }
-
- for (n = 0; n < nodecount; n++) {
- for (i = n - cldcnt[n]; i <= n; i++) {
- for (j = 0; j < (n - cldcnt[n]); j++) {
- map[j*64 + i] = map[i*64 + j] =
- min(map[i*64 + j], speedcap[n]);
- }
- for (j = n + 1; j < nodecount; j++) {
- map[j*64 + i] = map[i*64 + j] =
- min(map[i*64 + j], speedcap[n]);
- }
- }
- }
-
- /* assume a maximum speed for 1394b PHYs, nodemgr will correct it */
- if (local_link_speed > SELFID_SPEED_UNKNOWN)
- for (i = 0; i < nodecount; i++)
- if (speedcap[i] == SELFID_SPEED_UNKNOWN)
- speedcap[i] = local_link_speed;
-}
-
-
-/**
- * hpsb_selfid_received - hand over received selfid packet to the core
- *
- * For host driver module usage. Safe to use in interrupt context.
- *
- * The host driver should have done a successful complement check (second
- * quadlet is complement of first) beforehand.
- */
-void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid)
-{
- if (host->in_bus_reset) {
- HPSB_VERBOSE("Including SelfID 0x%x", sid);
- host->topology_map[host->selfid_count++] = sid;
- } else {
- HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d",
- sid, NODEID_TO_BUS(host->node_id));
- }
-}
-
-/**
- * hpsb_selfid_complete - notify completion of SelfID stage to the core
- *
- * For host driver module usage. Safe to use in interrupt context, although
- * quite complex; so you may want to run it in the bottom rather than top half.
- *
- * Notify completion of SelfID stage to the core and report new physical ID
- * and whether host is root now.
- */
-void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
-{
- if (!host->in_bus_reset)
- HPSB_NOTICE("SelfID completion called outside of bus reset!");
-
- host->node_id = LOCAL_BUS | phyid;
- host->is_root = isroot;
-
- if (!check_selfids(host)) {
- if (host->reset_retries++ < 20) {
- /* selfid stage did not complete without error */
- HPSB_NOTICE("Error in SelfID stage, resetting");
- host->in_bus_reset = 0;
- /* this should work from ohci1394 now... */
- hpsb_reset_bus(host, LONG_RESET);
- return;
- } else {
- HPSB_NOTICE("Stopping out-of-control reset loop");
- HPSB_NOTICE("Warning - topology map and speed map will not be valid");
- host->reset_retries = 0;
- }
- } else {
- host->reset_retries = 0;
- build_speed_map(host, host->node_count);
- }
-
- HPSB_VERBOSE("selfid_complete called with successful SelfID stage "
- "... irm_id: 0x%X node_id: 0x%X",host->irm_id,host->node_id);
-
- /* irm_id is kept up to date by check_selfids() */
- if (host->irm_id == host->node_id) {
- host->is_irm = 1;
- } else {
- host->is_busmgr = 0;
- host->is_irm = 0;
- }
-
- if (isroot) {
- host->driver->devctl(host, ACT_CYCLE_MASTER, 1);
- host->is_cycmst = 1;
- }
- atomic_inc(&host->generation);
- host->in_bus_reset = 0;
- highlevel_host_reset(host);
-}
-
-static DEFINE_SPINLOCK(pending_packets_lock);
-
-/**
- * hpsb_packet_sent - notify core of sending a packet
- *
- * For host driver module usage. Safe to call from within a transmit packet
- * routine.
- *
- * Notify core of sending a packet. Ackcode is the ack code returned for async
- * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE
- * for other cases (internal errors that don't justify a panic).
- */
-void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
- int ackcode)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&pending_packets_lock, flags);
-
- packet->ack_code = ackcode;
-
- if (packet->no_waiter || packet->state == hpsb_complete) {
- /* if packet->no_waiter, must not have a tlabel allocated */
- spin_unlock_irqrestore(&pending_packets_lock, flags);
- hpsb_free_packet(packet);
- return;
- }
-
- atomic_dec(&packet->refcnt); /* drop HC's reference */
- /* here the packet must be on the host->pending_packets queue */
-
- if (ackcode != ACK_PENDING || !packet->expect_response) {
- packet->state = hpsb_complete;
- list_del_init(&packet->queue);
- spin_unlock_irqrestore(&pending_packets_lock, flags);
- queue_packet_complete(packet);
- return;
- }
-
- packet->state = hpsb_pending;
- packet->sendtime = jiffies;
-
- spin_unlock_irqrestore(&pending_packets_lock, flags);
-
- mod_timer(&host->timeout, jiffies + host->timeout_interval);
-}
-
-/**
- * hpsb_send_phy_config - transmit a PHY configuration packet on the bus
- * @host: host that PHY config packet gets sent through
- * @rootid: root whose force_root bit should get set (-1 = don't set force_root)
- * @gapcnt: gap count value to set (-1 = don't set gap count)
- *
- * This function sends a PHY config packet on the bus through the specified
- * host.
- *
- * Return value: 0 for success or negative error number otherwise.
- */
-int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt)
-{
- struct hpsb_packet *packet;
- quadlet_t d = 0;
- int retval = 0;
-
- if (rootid >= ALL_NODES || rootid < -1 || gapcnt > 0x3f || gapcnt < -1 ||
- (rootid == -1 && gapcnt == -1)) {
- HPSB_DEBUG("Invalid Parameter: rootid = %d gapcnt = %d",
- rootid, gapcnt);
- return -EINVAL;
- }
-
- if (rootid != -1)
- d |= PHYPACKET_PHYCONFIG_R | rootid << PHYPACKET_PORT_SHIFT;
- if (gapcnt != -1)
- d |= PHYPACKET_PHYCONFIG_T | gapcnt << PHYPACKET_GAPCOUNT_SHIFT;
-
- packet = hpsb_make_phypacket(host, d);
- if (!packet)
- return -ENOMEM;
-
- packet->generation = get_hpsb_generation(host);
- retval = hpsb_send_packet_and_wait(packet);
- hpsb_free_packet(packet);
-
- return retval;
-}
-
-/**
- * hpsb_send_packet - transmit a packet on the bus
- * @packet: packet to send
- *
- * The packet is sent through the host specified in the packet->host field.
- * Before sending, the packet's transmit speed is automatically determined
- * using the local speed map when it is an async, non-broadcast packet.
- *
- * Possibilities for failure are that host is either not initialized, in bus
- * reset, the packet's generation number doesn't match the current generation
- * number or the host reports a transmit error.
- *
- * Return value: 0 on success, negative errno on failure.
- */
-int hpsb_send_packet(struct hpsb_packet *packet)
-{
- struct hpsb_host *host = packet->host;
-
- if (host->is_shutdown)
- return -EINVAL;
- if (host->in_bus_reset ||
- (packet->generation != get_hpsb_generation(host)))
- return -EAGAIN;
-
- packet->state = hpsb_queued;
-
- /* This just seems silly to me */
- WARN_ON(packet->no_waiter && packet->expect_response);
-
- if (!packet->no_waiter || packet->expect_response) {
- unsigned long flags;
-
- atomic_inc(&packet->refcnt);
- /* Set the initial "sendtime" to 10 seconds from now, to
- prevent premature expiry. If a packet takes more than
- 10 seconds to hit the wire, we have bigger problems :) */
- packet->sendtime = jiffies + 10 * HZ;
- spin_lock_irqsave(&pending_packets_lock, flags);
- list_add_tail(&packet->queue, &host->pending_packets);
- spin_unlock_irqrestore(&pending_packets_lock, flags);
- }
-
- if (packet->node_id == host->node_id) {
- /* it is a local request, so handle it locally */
-
- quadlet_t *data;
- size_t size = packet->data_size + packet->header_size;
-
- data = kmalloc(size, GFP_ATOMIC);
- if (!data) {
- HPSB_ERR("unable to allocate memory for concatenating header and data");
- return -ENOMEM;
- }
-
- memcpy(data, packet->header, packet->header_size);
-
- if (packet->data_size)
- memcpy(((u8*)data) + packet->header_size, packet->data, packet->data_size);
-
- dump_packet("send packet local", packet->header, packet->header_size, -1);
-
- hpsb_packet_sent(host, packet, packet->expect_response ? ACK_PENDING : ACK_COMPLETE);
- hpsb_packet_received(host, data, size, 0);
-
- kfree(data);
-
- return 0;
- }
-
- if (packet->type == hpsb_async &&
- NODEID_TO_NODE(packet->node_id) != ALL_NODES)
- packet->speed_code =
- host->speed[NODEID_TO_NODE(packet->node_id)];
-
- dump_packet("send packet", packet->header, packet->header_size, packet->speed_code);
-
- return host->driver->transmit_packet(host, packet);
-}
-
-/* We could just use complete() directly as the packet complete
- * callback, but this is more typesafe, in the sense that we get a
- * compiler error if the prototype for complete() changes. */
-
-static void complete_packet(void *data)
-{
- complete((struct completion *) data);
-}
-
-/**
- * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes
- * @packet: packet to send
- *
- * Return value: 0 on success, negative errno on failure.
- */
-int hpsb_send_packet_and_wait(struct hpsb_packet *packet)
-{
- struct completion done;
- int retval;
-
- init_completion(&done);
- hpsb_set_packet_complete_task(packet, complete_packet, &done);
- retval = hpsb_send_packet(packet);
- if (retval == 0)
- wait_for_completion(&done);
-
- return retval;
-}
-
-static void send_packet_nocare(struct hpsb_packet *packet)
-{
- if (hpsb_send_packet(packet) < 0) {
- hpsb_free_packet(packet);
- }
-}
-
-static size_t packet_size_to_data_size(size_t packet_size, size_t header_size,
- size_t buffer_size, int tcode)
-{
- size_t ret = packet_size <= header_size ? 0 : packet_size - header_size;
-
- if (unlikely(ret > buffer_size))
- ret = buffer_size;
-
- if (unlikely(ret + header_size != packet_size))
- HPSB_ERR("unexpected packet size %zd (tcode %d), bug?",
- packet_size, tcode);
- return ret;
-}
-
-static void handle_packet_response(struct hpsb_host *host, int tcode,
- quadlet_t *data, size_t size)
-{
- struct hpsb_packet *packet;
- int tlabel = (data[0] >> 10) & 0x3f;
- size_t header_size;
- unsigned long flags;
-
- spin_lock_irqsave(&pending_packets_lock, flags);
-
- list_for_each_entry(packet, &host->pending_packets, queue)
- if (packet->tlabel == tlabel &&
- packet->node_id == (data[1] >> 16))
- goto found;
-
- spin_unlock_irqrestore(&pending_packets_lock, flags);
- HPSB_DEBUG("unsolicited response packet received - %s",
- "no tlabel match");
- dump_packet("contents", data, 16, -1);
- return;
-
-found:
- switch (packet->tcode) {
- case TCODE_WRITEQ:
- case TCODE_WRITEB:
- if (unlikely(tcode != TCODE_WRITE_RESPONSE))
- break;
- header_size = 12;
- size = 0;
- goto dequeue;
-
- case TCODE_READQ:
- if (unlikely(tcode != TCODE_READQ_RESPONSE))
- break;
- header_size = 16;
- size = 0;
- goto dequeue;
-
- case TCODE_READB:
- if (unlikely(tcode != TCODE_READB_RESPONSE))
- break;
- header_size = 16;
- size = packet_size_to_data_size(size, header_size,
- packet->allocated_data_size,
- tcode);
- goto dequeue;
-
- case TCODE_LOCK_REQUEST:
- if (unlikely(tcode != TCODE_LOCK_RESPONSE))
- break;
- header_size = 16;
- size = packet_size_to_data_size(min(size, (size_t)(16 + 8)),
- header_size,
- packet->allocated_data_size,
- tcode);
- goto dequeue;
- }
-
- spin_unlock_irqrestore(&pending_packets_lock, flags);
- HPSB_DEBUG("unsolicited response packet received - %s",
- "tcode mismatch");
- dump_packet("contents", data, 16, -1);
- return;
-
-dequeue:
- list_del_init(&packet->queue);
- spin_unlock_irqrestore(&pending_packets_lock, flags);
-
- if (packet->state == hpsb_queued) {
- packet->sendtime = jiffies;
- packet->ack_code = ACK_PENDING;
- }
- packet->state = hpsb_complete;
-
- memcpy(packet->header, data, header_size);
- if (size)
- memcpy(packet->data, data + 4, size);
-
- queue_packet_complete(packet);
-}
-
-
-static struct hpsb_packet *create_reply_packet(struct hpsb_host *host,
- quadlet_t *data, size_t dsize)
-{
- struct hpsb_packet *p;
-
- p = hpsb_alloc_packet(dsize);
- if (unlikely(p == NULL)) {
- /* FIXME - send data_error response */
- HPSB_ERR("out of memory, cannot send response packet");
- return NULL;
- }
-
- p->type = hpsb_async;
- p->state = hpsb_unused;
- p->host = host;
- p->node_id = data[1] >> 16;
- p->tlabel = (data[0] >> 10) & 0x3f;
- p->no_waiter = 1;
-
- p->generation = get_hpsb_generation(host);
-
- if (dsize % 4)
- p->data[dsize / 4] = 0;
-
- return p;
-}
-
-#define PREP_ASYNC_HEAD_RCODE(tc) \
- packet->tcode = tc; \
- packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \
- | (1 << 8) | (tc << 4); \
- packet->header[1] = (packet->host->node_id << 16) | (rcode << 12); \
- packet->header[2] = 0
-
-static void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode,
- quadlet_t data)
-{
- PREP_ASYNC_HEAD_RCODE(TCODE_READQ_RESPONSE);
- packet->header[3] = data;
- packet->header_size = 16;
- packet->data_size = 0;
-}
-
-static void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode,
- int length)
-{
- if (rcode != RCODE_COMPLETE)
- length = 0;
-
- PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE);
- packet->header[3] = length << 16;
- packet->header_size = 16;
- packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0);
-}
-
-static void fill_async_write_resp(struct hpsb_packet *packet, int rcode)
-{
- PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE);
- packet->header_size = 12;
- packet->data_size = 0;
-}
-
-static void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode,
- int length)
-{
- if (rcode != RCODE_COMPLETE)
- length = 0;
-
- PREP_ASYNC_HEAD_RCODE(TCODE_LOCK_RESPONSE);
- packet->header[3] = (length << 16) | extcode;
- packet->header_size = 16;
- packet->data_size = length;
-}
-
-static void handle_incoming_packet(struct hpsb_host *host, int tcode,
- quadlet_t *data, size_t size,
- int write_acked)
-{
- struct hpsb_packet *packet;
- int length, rcode, extcode;
- quadlet_t buffer;
- nodeid_t source = data[1] >> 16;
- nodeid_t dest = data[0] >> 16;
- u16 flags = (u16) data[0];
- u64 addr;
-
- /* FIXME?
- * Out-of-bounds lengths are left for highlevel_read|write to cap. */
-
- switch (tcode) {
- case TCODE_WRITEQ:
- addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
- rcode = highlevel_write(host, source, dest, data + 3,
- addr, 4, flags);
- goto handle_write_request;
-
- case TCODE_WRITEB:
- addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
- rcode = highlevel_write(host, source, dest, data + 4,
- addr, data[3] >> 16, flags);
-handle_write_request:
- if (rcode < 0 || write_acked ||
- NODEID_TO_NODE(data[0] >> 16) == NODE_MASK)
- return;
- /* not a broadcast write, reply */
- packet = create_reply_packet(host, data, 0);
- if (packet) {
- fill_async_write_resp(packet, rcode);
- send_packet_nocare(packet);
- }
- return;
-
- case TCODE_READQ:
- addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
- rcode = highlevel_read(host, source, &buffer, addr, 4, flags);
- if (rcode < 0)
- return;
-
- packet = create_reply_packet(host, data, 0);
- if (packet) {
- fill_async_readquad_resp(packet, rcode, buffer);
- send_packet_nocare(packet);
- }
- return;
-
- case TCODE_READB:
- length = data[3] >> 16;
- packet = create_reply_packet(host, data, length);
- if (!packet)
- return;
-
- addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
- rcode = highlevel_read(host, source, packet->data, addr,
- length, flags);
- if (rcode < 0) {
- hpsb_free_packet(packet);
- return;
- }
- fill_async_readblock_resp(packet, rcode, length);
- send_packet_nocare(packet);
- return;
-
- case TCODE_LOCK_REQUEST:
- length = data[3] >> 16;
- extcode = data[3] & 0xffff;
- addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
-
- packet = create_reply_packet(host, data, 8);
- if (!packet)
- return;
-
- if (extcode == 0 || extcode >= 7) {
- /* let switch default handle error */
- length = 0;
- }
-
- switch (length) {
- case 4:
- rcode = highlevel_lock(host, source, packet->data, addr,
- data[4], 0, extcode, flags);
- fill_async_lock_resp(packet, rcode, extcode, 4);
- break;
- case 8:
- if (extcode != EXTCODE_FETCH_ADD &&
- extcode != EXTCODE_LITTLE_ADD) {
- rcode = highlevel_lock(host, source,
- packet->data, addr,
- data[5], data[4],
- extcode, flags);
- fill_async_lock_resp(packet, rcode, extcode, 4);
- } else {
- rcode = highlevel_lock64(host, source,
- (octlet_t *)packet->data, addr,
- *(octlet_t *)(data + 4), 0ULL,
- extcode, flags);
- fill_async_lock_resp(packet, rcode, extcode, 8);
- }
- break;
- case 16:
- rcode = highlevel_lock64(host, source,
- (octlet_t *)packet->data, addr,
- *(octlet_t *)(data + 6),
- *(octlet_t *)(data + 4),
- extcode, flags);
- fill_async_lock_resp(packet, rcode, extcode, 8);
- break;
- default:
- rcode = RCODE_TYPE_ERROR;
- fill_async_lock_resp(packet, rcode, extcode, 0);
- }
-
- if (rcode < 0)
- hpsb_free_packet(packet);
- else
- send_packet_nocare(packet);
- return;
- }
-}
-
-/**
- * hpsb_packet_received - hand over received packet to the core
- *
- * For host driver module usage.
- *
- * The contents of data are expected to be the full packet but with the CRCs
- * left out (data block follows header immediately), with the header (i.e. the
- * first four quadlets) in machine byte order and the data block in big endian.
- * *@data can be safely overwritten after this call.
- *
- * If the packet is a write request, @write_acked is to be set to true if it was
- * ack_complete'd already, false otherwise. This argument is ignored for any
- * other packet type.
- */
-void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
- int write_acked)
-{
- int tcode;
-
- if (unlikely(host->in_bus_reset)) {
- HPSB_DEBUG("received packet during reset; ignoring");
- return;
- }
-
- dump_packet("received packet", data, size, -1);
-
- tcode = (data[0] >> 4) & 0xf;
-
- switch (tcode) {
- case TCODE_WRITE_RESPONSE:
- case TCODE_READQ_RESPONSE:
- case TCODE_READB_RESPONSE:
- case TCODE_LOCK_RESPONSE:
- handle_packet_response(host, tcode, data, size);
- break;
-
- case TCODE_WRITEQ:
- case TCODE_WRITEB:
- case TCODE_READQ:
- case TCODE_READB:
- case TCODE_LOCK_REQUEST:
- handle_incoming_packet(host, tcode, data, size, write_acked);
- break;
-
- case TCODE_CYCLE_START:
- /* simply ignore this packet if it is passed on */
- break;
-
- default:
- HPSB_DEBUG("received packet with bogus transaction code %d",
- tcode);
- break;
- }
-}
-
-static void abort_requests(struct hpsb_host *host)
-{
- struct hpsb_packet *packet, *p;
- struct list_head tmp;
- unsigned long flags;
-
- host->driver->devctl(host, CANCEL_REQUESTS, 0);
-
- INIT_LIST_HEAD(&tmp);
- spin_lock_irqsave(&pending_packets_lock, flags);
- list_splice_init(&host->pending_packets, &tmp);
- spin_unlock_irqrestore(&pending_packets_lock, flags);
-
- list_for_each_entry_safe(packet, p, &tmp, queue) {
- list_del_init(&packet->queue);
- packet->state = hpsb_complete;
- packet->ack_code = ACKX_ABORTED;
- queue_packet_complete(packet);
- }
-}
-
-void abort_timedouts(unsigned long __opaque)
-{
- struct hpsb_host *host = (struct hpsb_host *)__opaque;
- struct hpsb_packet *packet, *p;
- struct list_head tmp;
- unsigned long flags, expire, j;
-
- spin_lock_irqsave(&host->csr.lock, flags);
- expire = host->csr.expire;
- spin_unlock_irqrestore(&host->csr.lock, flags);
-
- j = jiffies;
- INIT_LIST_HEAD(&tmp);
- spin_lock_irqsave(&pending_packets_lock, flags);
-
- list_for_each_entry_safe(packet, p, &host->pending_packets, queue) {
- if (time_before(packet->sendtime + expire, j))
- list_move_tail(&packet->queue, &tmp);
- else
- /* Since packets are added to the tail, the oldest
- * ones are first, always. When we get to one that
- * isn't timed out, the rest aren't either. */
- break;
- }
- if (!list_empty(&host->pending_packets))
- mod_timer(&host->timeout, j + host->timeout_interval);
-
- spin_unlock_irqrestore(&pending_packets_lock, flags);
-
- list_for_each_entry_safe(packet, p, &tmp, queue) {
- list_del_init(&packet->queue);
- packet->state = hpsb_complete;
- packet->ack_code = ACKX_TIMEOUT;
- queue_packet_complete(packet);
- }
-}
-
-static struct task_struct *khpsbpkt_thread;
-static LIST_HEAD(hpsbpkt_queue);
-
-static void queue_packet_complete(struct hpsb_packet *packet)
-{
- unsigned long flags;
-
- if (packet->no_waiter) {
- hpsb_free_packet(packet);
- return;
- }
- if (packet->complete_routine != NULL) {
- spin_lock_irqsave(&pending_packets_lock, flags);
- list_add_tail(&packet->queue, &hpsbpkt_queue);
- spin_unlock_irqrestore(&pending_packets_lock, flags);
- wake_up_process(khpsbpkt_thread);
- }
- return;
-}
-
-/*
- * Kernel thread which handles packets that are completed. This way the
- * packet's "complete" function is asynchronously run in process context.
- * Only packets which have a "complete" function may be sent here.
- */
-static int hpsbpkt_thread(void *__hi)
-{
- struct hpsb_packet *packet, *p;
- struct list_head tmp;
- int may_schedule;
-
- while (!kthread_should_stop()) {
-
- INIT_LIST_HEAD(&tmp);
- spin_lock_irq(&pending_packets_lock);
- list_splice_init(&hpsbpkt_queue, &tmp);
- spin_unlock_irq(&pending_packets_lock);
-
- list_for_each_entry_safe(packet, p, &tmp, queue) {
- list_del_init(&packet->queue);
- packet->complete_routine(packet->complete_data);
- }
-
- set_current_state(TASK_INTERRUPTIBLE);
- spin_lock_irq(&pending_packets_lock);
- may_schedule = list_empty(&hpsbpkt_queue);
- spin_unlock_irq(&pending_packets_lock);
- if (may_schedule)
- schedule();
- __set_current_state(TASK_RUNNING);
- }
- return 0;
-}
-
-static int __init ieee1394_init(void)
-{
- int i, ret;
-
- /* non-fatal error */
- if (hpsb_init_config_roms()) {
- HPSB_ERR("Failed to initialize some config rom entries.\n");
- HPSB_ERR("Some features may not be available\n");
- }
-
- khpsbpkt_thread = kthread_run(hpsbpkt_thread, NULL, "khpsbpkt");
- if (IS_ERR(khpsbpkt_thread)) {
- HPSB_ERR("Failed to start hpsbpkt thread!\n");
- ret = PTR_ERR(khpsbpkt_thread);
- goto exit_cleanup_config_roms;
- }
-
- if (register_chrdev_region(IEEE1394_CORE_DEV, 256, "ieee1394")) {
- HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR);
- ret = -ENODEV;
- goto exit_release_kernel_thread;
- }
-
- ret = bus_register(&ieee1394_bus_type);
- if (ret < 0) {
- HPSB_INFO("bus register failed");
- goto release_chrdev;
- }
-
- for (i = 0; fw_bus_attrs[i]; i++) {
- ret = bus_create_file(&ieee1394_bus_type, fw_bus_attrs[i]);
- if (ret < 0) {
- while (i >= 0) {
- bus_remove_file(&ieee1394_bus_type,
- fw_bus_attrs[i--]);
- }
- bus_unregister(&ieee1394_bus_type);
- goto release_chrdev;
- }
- }
-
- ret = class_register(&hpsb_host_class);
- if (ret < 0)
- goto release_all_bus;
-
- hpsb_protocol_class = class_create(THIS_MODULE, "ieee1394_protocol");
- if (IS_ERR(hpsb_protocol_class)) {
- ret = PTR_ERR(hpsb_protocol_class);
- goto release_class_host;
- }
-
- ret = init_csr();
- if (ret) {
- HPSB_INFO("init csr failed");
- ret = -ENOMEM;
- goto release_class_protocol;
- }
-
- if (disable_nodemgr) {
- HPSB_INFO("nodemgr and IRM functionality disabled");
- /* We shouldn't contend for IRM with nodemgr disabled, since
- nodemgr implements functionality required of ieee1394a-2000
- IRMs */
- hpsb_disable_irm = 1;
-
- return 0;
- }
-
- if (hpsb_disable_irm) {
- HPSB_INFO("IRM functionality disabled");
- }
-
- ret = init_ieee1394_nodemgr();
- if (ret < 0) {
- HPSB_INFO("init nodemgr failed");
- goto cleanup_csr;
- }
-
- return 0;
-
-cleanup_csr:
- cleanup_csr();
-release_class_protocol:
- class_destroy(hpsb_protocol_class);
-release_class_host:
- class_unregister(&hpsb_host_class);
-release_all_bus:
- for (i = 0; fw_bus_attrs[i]; i++)
- bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]);
- bus_unregister(&ieee1394_bus_type);
-release_chrdev:
- unregister_chrdev_region(IEEE1394_CORE_DEV, 256);
-exit_release_kernel_thread:
- kthread_stop(khpsbpkt_thread);
-exit_cleanup_config_roms:
- hpsb_cleanup_config_roms();
- return ret;
-}
-
-static void __exit ieee1394_cleanup(void)
-{
- int i;
-
- if (!disable_nodemgr)
- cleanup_ieee1394_nodemgr();
-
- cleanup_csr();
-
- class_destroy(hpsb_protocol_class);
- class_unregister(&hpsb_host_class);
- for (i = 0; fw_bus_attrs[i]; i++)
- bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]);
- bus_unregister(&ieee1394_bus_type);
-
- kthread_stop(khpsbpkt_thread);
-
- hpsb_cleanup_config_roms();
-
- unregister_chrdev_region(IEEE1394_CORE_DEV, 256);
-}
-
-fs_initcall(ieee1394_init);
-module_exit(ieee1394_cleanup);
-
-/* Exported symbols */
-
-/** hosts.c **/
-EXPORT_SYMBOL(hpsb_alloc_host);
-EXPORT_SYMBOL(hpsb_add_host);
-EXPORT_SYMBOL(hpsb_resume_host);
-EXPORT_SYMBOL(hpsb_remove_host);
-EXPORT_SYMBOL(hpsb_update_config_rom_image);
-
-/** ieee1394_core.c **/
-EXPORT_SYMBOL(hpsb_speedto_str);
-EXPORT_SYMBOL(hpsb_protocol_class);
-EXPORT_SYMBOL(hpsb_set_packet_complete_task);
-EXPORT_SYMBOL(hpsb_alloc_packet);
-EXPORT_SYMBOL(hpsb_free_packet);
-EXPORT_SYMBOL(hpsb_send_packet);
-EXPORT_SYMBOL(hpsb_reset_bus);
-EXPORT_SYMBOL(hpsb_read_cycle_timer);
-EXPORT_SYMBOL(hpsb_bus_reset);
-EXPORT_SYMBOL(hpsb_selfid_received);
-EXPORT_SYMBOL(hpsb_selfid_complete);
-EXPORT_SYMBOL(hpsb_packet_sent);
-EXPORT_SYMBOL(hpsb_packet_received);
-EXPORT_SYMBOL_GPL(hpsb_disable_irm);
-
-/** ieee1394_transactions.c **/
-EXPORT_SYMBOL(hpsb_get_tlabel);
-EXPORT_SYMBOL(hpsb_free_tlabel);
-EXPORT_SYMBOL(hpsb_make_readpacket);
-EXPORT_SYMBOL(hpsb_make_writepacket);
-EXPORT_SYMBOL(hpsb_make_streampacket);
-EXPORT_SYMBOL(hpsb_make_lockpacket);
-EXPORT_SYMBOL(hpsb_make_lock64packet);
-EXPORT_SYMBOL(hpsb_make_phypacket);
-EXPORT_SYMBOL(hpsb_read);
-EXPORT_SYMBOL(hpsb_write);
-EXPORT_SYMBOL(hpsb_lock);
-EXPORT_SYMBOL(hpsb_packet_success);
-
-/** highlevel.c **/
-EXPORT_SYMBOL(hpsb_register_highlevel);
-EXPORT_SYMBOL(hpsb_unregister_highlevel);
-EXPORT_SYMBOL(hpsb_register_addrspace);
-EXPORT_SYMBOL(hpsb_unregister_addrspace);
-EXPORT_SYMBOL(hpsb_allocate_and_register_addrspace);
-EXPORT_SYMBOL(hpsb_get_hostinfo);
-EXPORT_SYMBOL(hpsb_create_hostinfo);
-EXPORT_SYMBOL(hpsb_destroy_hostinfo);
-EXPORT_SYMBOL(hpsb_set_hostinfo_key);
-EXPORT_SYMBOL(hpsb_get_hostinfo_bykey);
-EXPORT_SYMBOL(hpsb_set_hostinfo);
-
-/** nodemgr.c **/
-EXPORT_SYMBOL(hpsb_node_fill_packet);
-EXPORT_SYMBOL(hpsb_node_write);
-EXPORT_SYMBOL(__hpsb_register_protocol);
-EXPORT_SYMBOL(hpsb_unregister_protocol);
-
-/** csr.c **/
-EXPORT_SYMBOL(hpsb_update_config_rom);
-
-/** dma.c **/
-EXPORT_SYMBOL(dma_prog_region_init);
-EXPORT_SYMBOL(dma_prog_region_alloc);
-EXPORT_SYMBOL(dma_prog_region_free);
-EXPORT_SYMBOL(dma_region_init);
-EXPORT_SYMBOL(dma_region_alloc);
-EXPORT_SYMBOL(dma_region_free);
-EXPORT_SYMBOL(dma_region_sync_for_cpu);
-EXPORT_SYMBOL(dma_region_sync_for_device);
-EXPORT_SYMBOL(dma_region_mmap);
-EXPORT_SYMBOL(dma_region_offset_to_bus);
-
-/** iso.c **/
-EXPORT_SYMBOL(hpsb_iso_xmit_init);
-EXPORT_SYMBOL(hpsb_iso_recv_init);
-EXPORT_SYMBOL(hpsb_iso_xmit_start);
-EXPORT_SYMBOL(hpsb_iso_recv_start);
-EXPORT_SYMBOL(hpsb_iso_recv_listen_channel);
-EXPORT_SYMBOL(hpsb_iso_recv_unlisten_channel);
-EXPORT_SYMBOL(hpsb_iso_recv_set_channel_mask);
-EXPORT_SYMBOL(hpsb_iso_stop);
-EXPORT_SYMBOL(hpsb_iso_shutdown);
-EXPORT_SYMBOL(hpsb_iso_xmit_queue_packet);
-EXPORT_SYMBOL(hpsb_iso_xmit_sync);
-EXPORT_SYMBOL(hpsb_iso_recv_release_packets);
-EXPORT_SYMBOL(hpsb_iso_n_ready);
-EXPORT_SYMBOL(hpsb_iso_packet_sent);
-EXPORT_SYMBOL(hpsb_iso_packet_received);
-EXPORT_SYMBOL(hpsb_iso_wake);
-EXPORT_SYMBOL(hpsb_iso_recv_flush);
-
-/** csr1212.c **/
-EXPORT_SYMBOL(csr1212_attach_keyval_to_directory);
-EXPORT_SYMBOL(csr1212_detach_keyval_from_directory);
-EXPORT_SYMBOL(csr1212_get_keyval);
-EXPORT_SYMBOL(csr1212_new_directory);
-EXPORT_SYMBOL(csr1212_parse_keyval);
-EXPORT_SYMBOL(csr1212_read);
-EXPORT_SYMBOL(csr1212_release_keyval);
diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
deleted file mode 100644
index 28b9f58bafd2..000000000000
--- a/drivers/ieee1394/ieee1394_core.h
+++ /dev/null
@@ -1,172 +0,0 @@
-#ifndef _IEEE1394_CORE_H
-#define _IEEE1394_CORE_H
-
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/list.h>
-#include <linux/types.h>
-#include <linux/cdev.h>
-#include <asm/atomic.h>
-
-#include "hosts.h"
-#include "ieee1394_types.h"
-
-struct hpsb_packet {
- /* This struct is basically read-only for hosts with the exception of
- * the data buffer contents and driver_list. */
-
- /* This can be used for host driver internal linking.
- *
- * NOTE: This must be left in init state when the driver is done
- * with it (e.g. by using list_del_init()), since the core does
- * some sanity checks to make sure the packet is not on a
- * driver_list when free'ing it. */
- struct list_head driver_list;
-
- nodeid_t node_id;
-
- /* hpsb_raw = send as-is, do not CRC (but still byte-swap it) */
- enum { hpsb_async, hpsb_raw } __attribute__((packed)) type;
-
- /* Okay, this is core internal and a no care for hosts.
- * queued = queued for sending
- * pending = sent, waiting for response
- * complete = processing completed, successful or not
- */
- enum {
- hpsb_unused, hpsb_queued, hpsb_pending, hpsb_complete
- } __attribute__((packed)) state;
-
- /* These are core-internal. */
- signed char tlabel;
- signed char ack_code;
- unsigned char tcode;
-
- unsigned expect_response:1;
- unsigned no_waiter:1;
-
- /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */
- unsigned speed_code:2;
-
- struct hpsb_host *host;
- unsigned int generation;
-
- atomic_t refcnt;
- struct list_head queue;
-
- /* Function (and possible data to pass to it) to call when this
- * packet is completed. */
- void (*complete_routine)(void *);
- void *complete_data;
-
- /* Store jiffies for implementing bus timeouts. */
- unsigned long sendtime;
-
- /* Core-internal. */
- size_t allocated_data_size; /* as allocated */
-
- /* Sizes are in bytes. To be set by caller of hpsb_alloc_packet. */
- size_t data_size; /* as filled in */
- size_t header_size; /* as filled in, not counting the CRC */
-
- /* Buffers */
- quadlet_t *data; /* can be DMA-mapped */
- quadlet_t header[5];
- quadlet_t embedded_data[0]; /* keep as last member */
-};
-
-void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
- void (*routine)(void *), void *data);
-static inline struct hpsb_packet *driver_packet(struct list_head *l)
-{
- return list_entry(l, struct hpsb_packet, driver_list);
-}
-void abort_timedouts(unsigned long __opaque);
-struct hpsb_packet *hpsb_alloc_packet(size_t data_size);
-void hpsb_free_packet(struct hpsb_packet *packet);
-
-/**
- * get_hpsb_generation - generation counter for the complete 1394 subsystem
- *
- * Generation gets incremented on every change in the subsystem (notably on bus
- * resets). Use the functions, not the variable.
- */
-static inline unsigned int get_hpsb_generation(struct hpsb_host *host)
-{
- return atomic_read(&host->generation);
-}
-
-int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt);
-int hpsb_send_packet(struct hpsb_packet *packet);
-int hpsb_send_packet_and_wait(struct hpsb_packet *packet);
-int hpsb_reset_bus(struct hpsb_host *host, int type);
-int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer,
- u64 *local_time);
-
-int hpsb_bus_reset(struct hpsb_host *host);
-void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid);
-void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot);
-void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
- int ackcode);
-void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
- int write_acked);
-
-/*
- * CHARACTER DEVICE DISPATCHING
- *
- * All ieee1394 character device drivers share the same major number
- * (major 171). The 256 minor numbers are allocated to the various
- * task-specific interfaces (raw1394, video1394, dv1394, etc) in
- * blocks of 16.
- *
- * The core ieee1394.o module allocates the device number region
- * 171:0-255, the various drivers must then cdev_add() their cdev
- * objects to handle their respective sub-regions.
- *
- * Minor device number block allocations:
- *
- * Block 0 ( 0- 15) raw1394
- * Block 1 ( 16- 31) video1394
- * Block 2 ( 32- 47) dv1394
- *
- * Blocks 3-14 free for future allocation
- *
- * Block 15 (240-255) reserved for drivers under development, etc.
- */
-
-#define IEEE1394_MAJOR 171
-
-#define IEEE1394_MINOR_BLOCK_RAW1394 0
-#define IEEE1394_MINOR_BLOCK_VIDEO1394 1
-#define IEEE1394_MINOR_BLOCK_DV1394 2
-#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15
-
-#define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0)
-#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \
- IEEE1394_MINOR_BLOCK_RAW1394 * 16)
-#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \
- IEEE1394_MINOR_BLOCK_VIDEO1394 * 16)
-#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \
- IEEE1394_MINOR_BLOCK_DV1394 * 16)
-#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \
- IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16)
-
-/**
- * ieee1394_file_to_instance - get the index within a minor number block
- */
-static inline unsigned char ieee1394_file_to_instance(struct file *file)
-{
- int idx = cdev_index(file->f_path.dentry->d_inode);
- if (idx < 0)
- idx = 0;
- return idx;
-}
-
-extern int hpsb_disable_irm;
-
-/* Our sysfs bus entry */
-extern struct bus_type ieee1394_bus_type;
-extern struct class hpsb_host_class;
-extern struct class *hpsb_protocol_class;
-
-#endif /* _IEEE1394_CORE_H */
diff --git a/drivers/ieee1394/ieee1394_hotplug.h b/drivers/ieee1394/ieee1394_hotplug.h
deleted file mode 100644
index dd5500ed8322..000000000000
--- a/drivers/ieee1394/ieee1394_hotplug.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef _IEEE1394_HOTPLUG_H
-#define _IEEE1394_HOTPLUG_H
-
-/* Unit spec id and sw version entry for some protocols */
-#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D
-#define AVC_SW_VERSION_ENTRY 0x00010001
-#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D
-#define CAMERA_SW_VERSION_ENTRY 0x00000100
-
-/* /include/linux/mod_devicetable.h defines:
- * IEEE1394_MATCH_VENDOR_ID
- * IEEE1394_MATCH_MODEL_ID
- * IEEE1394_MATCH_SPECIFIER_ID
- * IEEE1394_MATCH_VERSION
- * struct ieee1394_device_id
- */
-#include <linux/mod_devicetable.h>
-
-#endif /* _IEEE1394_HOTPLUG_H */
diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c
deleted file mode 100644
index 675b3135d5f1..000000000000
--- a/drivers/ieee1394/ieee1394_transactions.c
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * Transaction support.
- *
- * Copyright (C) 1999 Andreas E. Bombe
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/bitops.h>
-#include <linux/compiler.h>
-#include <linux/hardirq.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/sched.h> /* because linux/wait.h is broken if CONFIG_SMP=n */
-#include <linux/wait.h>
-
-#include <asm/bug.h>
-#include <asm/errno.h>
-#include <asm/system.h>
-
-#include "ieee1394.h"
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "ieee1394_transactions.h"
-
-#define PREP_ASYNC_HEAD_ADDRESS(tc) \
- packet->tcode = tc; \
- packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \
- | (1 << 8) | (tc << 4); \
- packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \
- packet->header[2] = addr & 0xffffffff
-
-#ifndef HPSB_DEBUG_TLABELS
-static
-#endif
-DEFINE_SPINLOCK(hpsb_tlabel_lock);
-
-static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq);
-
-static void fill_async_readquad(struct hpsb_packet *packet, u64 addr)
-{
- PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ);
- packet->header_size = 12;
- packet->data_size = 0;
- packet->expect_response = 1;
-}
-
-static void fill_async_readblock(struct hpsb_packet *packet, u64 addr,
- int length)
-{
- PREP_ASYNC_HEAD_ADDRESS(TCODE_READB);
- packet->header[3] = length << 16;
- packet->header_size = 16;
- packet->data_size = 0;
- packet->expect_response = 1;
-}
-
-static void fill_async_writequad(struct hpsb_packet *packet, u64 addr,
- quadlet_t data)
-{
- PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEQ);
- packet->header[3] = data;
- packet->header_size = 16;
- packet->data_size = 0;
- packet->expect_response = 1;
-}
-
-static void fill_async_writeblock(struct hpsb_packet *packet, u64 addr,
- int length)
-{
- PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEB);
- packet->header[3] = length << 16;
- packet->header_size = 16;
- packet->expect_response = 1;
- packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0);
-}
-
-static void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode,
- int length)
-{
- PREP_ASYNC_HEAD_ADDRESS(TCODE_LOCK_REQUEST);
- packet->header[3] = (length << 16) | extcode;
- packet->header_size = 16;
- packet->data_size = length;
- packet->expect_response = 1;
-}
-
-static void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data)
-{
- packet->header[0] = data;
- packet->header[1] = ~data;
- packet->header_size = 8;
- packet->data_size = 0;
- packet->expect_response = 0;
- packet->type = hpsb_raw; /* No CRC added */
- packet->speed_code = IEEE1394_SPEED_100; /* Force speed to be 100Mbps */
-}
-
-static void fill_async_stream_packet(struct hpsb_packet *packet, int length,
- int channel, int tag, int sync)
-{
- packet->header[0] = (length << 16) | (tag << 14) | (channel << 8)
- | (TCODE_STREAM_DATA << 4) | sync;
-
- packet->header_size = 4;
- packet->data_size = length;
- packet->type = hpsb_async;
- packet->tcode = TCODE_ISO_DATA;
-}
-
-/* same as hpsb_get_tlabel, except that it returns immediately */
-static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet)
-{
- unsigned long flags, *tp;
- u8 *next;
- int tlabel, n = NODEID_TO_NODE(packet->node_id);
-
- /* Broadcast transactions are complete once the request has been sent.
- * Use the same transaction label for all broadcast transactions. */
- if (unlikely(n == ALL_NODES)) {
- packet->tlabel = 0;
- return 0;
- }
- tp = packet->host->tl_pool[n].map;
- next = &packet->host->next_tl[n];
-
- spin_lock_irqsave(&hpsb_tlabel_lock, flags);
- tlabel = find_next_zero_bit(tp, 64, *next);
- if (tlabel > 63)
- tlabel = find_first_zero_bit(tp, 64);
- if (tlabel > 63) {
- spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
- return -EAGAIN;
- }
- __set_bit(tlabel, tp);
- *next = (tlabel + 1) & 63;
- spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
-
- packet->tlabel = tlabel;
- return 0;
-}
-
-/**
- * hpsb_get_tlabel - allocate a transaction label
- * @packet: the packet whose tlabel and tl_pool we set
- *
- * Every asynchronous transaction on the 1394 bus needs a transaction
- * label to match the response to the request. This label has to be
- * different from any other transaction label in an outstanding request to
- * the same node to make matching possible without ambiguity.
- *
- * There are 64 different tlabels, so an allocated tlabel has to be freed
- * with hpsb_free_tlabel() after the transaction is complete (unless it's
- * reused again for the same target node).
- *
- * Return value: Zero on success, otherwise non-zero. A non-zero return
- * generally means there are no available tlabels. If this is called out
- * of interrupt or atomic context, then it will sleep until can return a
- * tlabel or a signal is received.
- */
-int hpsb_get_tlabel(struct hpsb_packet *packet)
-{
- if (irqs_disabled() || in_atomic())
- return hpsb_get_tlabel_atomic(packet);
-
- /* NB: The macro wait_event_interruptible() is called with a condition
- * argument with side effect. This is only possible because the side
- * effect does not occur until the condition became true, and
- * wait_event_interruptible() won't evaluate the condition again after
- * that. */
- return wait_event_interruptible(tlabel_wq,
- !hpsb_get_tlabel_atomic(packet));
-}
-
-/**
- * hpsb_free_tlabel - free an allocated transaction label
- * @packet: packet whose tlabel and tl_pool needs to be cleared
- *
- * Frees the transaction label allocated with hpsb_get_tlabel(). The
- * tlabel has to be freed after the transaction is complete (i.e. response
- * was received for a split transaction or packet was sent for a unified
- * transaction).
- *
- * A tlabel must not be freed twice.
- */
-void hpsb_free_tlabel(struct hpsb_packet *packet)
-{
- unsigned long flags, *tp;
- int tlabel, n = NODEID_TO_NODE(packet->node_id);
-
- if (unlikely(n == ALL_NODES))
- return;
- tp = packet->host->tl_pool[n].map;
- tlabel = packet->tlabel;
- BUG_ON(tlabel > 63 || tlabel < 0);
-
- spin_lock_irqsave(&hpsb_tlabel_lock, flags);
- BUG_ON(!__test_and_clear_bit(tlabel, tp));
- spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
-
- wake_up_interruptible(&tlabel_wq);
-}
-
-/**
- * hpsb_packet_success - Make sense of the ack and reply codes
- *
- * Make sense of the ack and reply codes and return more convenient error codes:
- * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can
- * probably resolved by retry. -%EREMOTEIO = node suffers from an internal
- * error. -%EACCES = this transaction is not allowed on requested address.
- * -%EINVAL = invalid address at node.
- */
-int hpsb_packet_success(struct hpsb_packet *packet)
-{
- switch (packet->ack_code) {
- case ACK_PENDING:
- switch ((packet->header[1] >> 12) & 0xf) {
- case RCODE_COMPLETE:
- return 0;
- case RCODE_CONFLICT_ERROR:
- return -EAGAIN;
- case RCODE_DATA_ERROR:
- return -EREMOTEIO;
- case RCODE_TYPE_ERROR:
- return -EACCES;
- case RCODE_ADDRESS_ERROR:
- return -EINVAL;
- default:
- HPSB_ERR("received reserved rcode %d from node %d",
- (packet->header[1] >> 12) & 0xf,
- packet->node_id);
- return -EAGAIN;
- }
-
- case ACK_BUSY_X:
- case ACK_BUSY_A:
- case ACK_BUSY_B:
- return -EBUSY;
-
- case ACK_TYPE_ERROR:
- return -EACCES;
-
- case ACK_COMPLETE:
- if (packet->tcode == TCODE_WRITEQ
- || packet->tcode == TCODE_WRITEB) {
- return 0;
- } else {
- HPSB_ERR("impossible ack_complete from node %d "
- "(tcode %d)", packet->node_id, packet->tcode);
- return -EAGAIN;
- }
-
- case ACK_DATA_ERROR:
- if (packet->tcode == TCODE_WRITEB
- || packet->tcode == TCODE_LOCK_REQUEST) {
- return -EAGAIN;
- } else {
- HPSB_ERR("impossible ack_data_error from node %d "
- "(tcode %d)", packet->node_id, packet->tcode);
- return -EAGAIN;
- }
-
- case ACK_ADDRESS_ERROR:
- return -EINVAL;
-
- case ACK_TARDY:
- case ACK_CONFLICT_ERROR:
- case ACKX_NONE:
- case ACKX_SEND_ERROR:
- case ACKX_ABORTED:
- case ACKX_TIMEOUT:
- /* error while sending */
- return -EAGAIN;
-
- default:
- HPSB_ERR("got invalid ack %d from node %d (tcode %d)",
- packet->ack_code, packet->node_id, packet->tcode);
- return -EAGAIN;
- }
-}
-
-struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
- u64 addr, size_t length)
-{
- struct hpsb_packet *packet;
-
- if (length == 0)
- return NULL;
-
- packet = hpsb_alloc_packet(length);
- if (!packet)
- return NULL;
-
- packet->host = host;
- packet->node_id = node;
-
- if (hpsb_get_tlabel(packet)) {
- hpsb_free_packet(packet);
- return NULL;
- }
-
- if (length == 4)
- fill_async_readquad(packet, addr);
- else
- fill_async_readblock(packet, addr, length);
-
- return packet;
-}
-
-struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, nodeid_t node,
- u64 addr, quadlet_t * buffer,
- size_t length)
-{
- struct hpsb_packet *packet;
-
- if (length == 0)
- return NULL;
-
- packet = hpsb_alloc_packet(length);
- if (!packet)
- return NULL;
-
- if (length % 4) { /* zero padding bytes */
- packet->data[length >> 2] = 0;
- }
- packet->host = host;
- packet->node_id = node;
-
- if (hpsb_get_tlabel(packet)) {
- hpsb_free_packet(packet);
- return NULL;
- }
-
- if (length == 4) {
- fill_async_writequad(packet, addr, buffer ? *buffer : 0);
- } else {
- fill_async_writeblock(packet, addr, length);
- if (buffer)
- memcpy(packet->data, buffer, length);
- }
-
- return packet;
-}
-
-struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 * buffer,
- int length, int channel, int tag,
- int sync)
-{
- struct hpsb_packet *packet;
-
- if (length == 0)
- return NULL;
-
- packet = hpsb_alloc_packet(length);
- if (!packet)
- return NULL;
-
- if (length % 4) { /* zero padding bytes */
- packet->data[length >> 2] = 0;
- }
- packet->host = host;
-
- /* Because it is too difficult to determine all PHY speeds and link
- * speeds here, we use S100... */
- packet->speed_code = IEEE1394_SPEED_100;
-
- /* ...and prevent hpsb_send_packet() from overriding it. */
- packet->node_id = LOCAL_BUS | ALL_NODES;
-
- if (hpsb_get_tlabel(packet)) {
- hpsb_free_packet(packet);
- return NULL;
- }
-
- fill_async_stream_packet(packet, length, channel, tag, sync);
- if (buffer)
- memcpy(packet->data, buffer, length);
-
- return packet;
-}
-
-struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node,
- u64 addr, int extcode,
- quadlet_t * data, quadlet_t arg)
-{
- struct hpsb_packet *p;
- u32 length;
-
- p = hpsb_alloc_packet(8);
- if (!p)
- return NULL;
-
- p->host = host;
- p->node_id = node;
- if (hpsb_get_tlabel(p)) {
- hpsb_free_packet(p);
- return NULL;
- }
-
- switch (extcode) {
- case EXTCODE_FETCH_ADD:
- case EXTCODE_LITTLE_ADD:
- length = 4;
- if (data)
- p->data[0] = *data;
- break;
- default:
- length = 8;
- if (data) {
- p->data[0] = arg;
- p->data[1] = *data;
- }
- break;
- }
- fill_async_lock(p, addr, extcode, length);
-
- return p;
-}
-
-struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host,
- nodeid_t node, u64 addr, int extcode,
- octlet_t * data, octlet_t arg)
-{
- struct hpsb_packet *p;
- u32 length;
-
- p = hpsb_alloc_packet(16);
- if (!p)
- return NULL;
-
- p->host = host;
- p->node_id = node;
- if (hpsb_get_tlabel(p)) {
- hpsb_free_packet(p);
- return NULL;
- }
-
- switch (extcode) {
- case EXTCODE_FETCH_ADD:
- case EXTCODE_LITTLE_ADD:
- length = 8;
- if (data) {
- p->data[0] = *data >> 32;
- p->data[1] = *data & 0xffffffff;
- }
- break;
- default:
- length = 16;
- if (data) {
- p->data[0] = arg >> 32;
- p->data[1] = arg & 0xffffffff;
- p->data[2] = *data >> 32;
- p->data[3] = *data & 0xffffffff;
- }
- break;
- }
- fill_async_lock(p, addr, extcode, length);
-
- return p;
-}
-
-struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data)
-{
- struct hpsb_packet *p;
-
- p = hpsb_alloc_packet(0);
- if (!p)
- return NULL;
-
- p->host = host;
- fill_phy_packet(p, data);
-
- return p;
-}
-
-/*
- * FIXME - these functions should probably read from / write to user space to
- * avoid in kernel buffers for user space callers
- */
-
-/**
- * hpsb_read - generic read function
- *
- * Recognizes the local node ID and act accordingly. Automatically uses a
- * quadlet read request if @length == 4 and and a block read request otherwise.
- * It does not yet support lengths that are not a multiple of 4.
- *
- * You must explicitly specifiy the @generation for which the node ID is valid,
- * to avoid sending packets to the wrong nodes when we race with a bus reset.
- */
-int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
- u64 addr, quadlet_t * buffer, size_t length)
-{
- struct hpsb_packet *packet;
- int retval = 0;
-
- if (length == 0)
- return -EINVAL;
-
- packet = hpsb_make_readpacket(host, node, addr, length);
-
- if (!packet) {
- return -ENOMEM;
- }
-
- packet->generation = generation;
- retval = hpsb_send_packet_and_wait(packet);
- if (retval < 0)
- goto hpsb_read_fail;
-
- retval = hpsb_packet_success(packet);
-
- if (retval == 0) {
- if (length == 4) {
- *buffer = packet->header[3];
- } else {
- memcpy(buffer, packet->data, length);
- }
- }
-
- hpsb_read_fail:
- hpsb_free_tlabel(packet);
- hpsb_free_packet(packet);
-
- return retval;
-}
-
-/**
- * hpsb_write - generic write function
- *
- * Recognizes the local node ID and act accordingly. Automatically uses a
- * quadlet write request if @length == 4 and and a block write request
- * otherwise. It does not yet support lengths that are not a multiple of 4.
- *
- * You must explicitly specifiy the @generation for which the node ID is valid,
- * to avoid sending packets to the wrong nodes when we race with a bus reset.
- */
-int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
- u64 addr, quadlet_t * buffer, size_t length)
-{
- struct hpsb_packet *packet;
- int retval;
-
- if (length == 0)
- return -EINVAL;
-
- packet = hpsb_make_writepacket(host, node, addr, buffer, length);
-
- if (!packet)
- return -ENOMEM;
-
- packet->generation = generation;
- retval = hpsb_send_packet_and_wait(packet);
- if (retval < 0)
- goto hpsb_write_fail;
-
- retval = hpsb_packet_success(packet);
-
- hpsb_write_fail:
- hpsb_free_tlabel(packet);
- hpsb_free_packet(packet);
-
- return retval;
-}
-
-int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation,
- u64 addr, int extcode, quadlet_t *data, quadlet_t arg)
-{
- struct hpsb_packet *packet;
- int retval = 0;
-
- packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg);
- if (!packet)
- return -ENOMEM;
-
- packet->generation = generation;
- retval = hpsb_send_packet_and_wait(packet);
- if (retval < 0)
- goto hpsb_lock_fail;
-
- retval = hpsb_packet_success(packet);
-
- if (retval == 0)
- *data = packet->data[0];
-
-hpsb_lock_fail:
- hpsb_free_tlabel(packet);
- hpsb_free_packet(packet);
-
- return retval;
-}
diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h
deleted file mode 100644
index 20b693be14b2..000000000000
--- a/drivers/ieee1394/ieee1394_transactions.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef _IEEE1394_TRANSACTIONS_H
-#define _IEEE1394_TRANSACTIONS_H
-
-#include <linux/types.h>
-
-#include "ieee1394_types.h"
-
-struct hpsb_packet;
-struct hpsb_host;
-
-int hpsb_get_tlabel(struct hpsb_packet *packet);
-void hpsb_free_tlabel(struct hpsb_packet *packet);
-struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
- u64 addr, size_t length);
-struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node,
- u64 addr, int extcode, quadlet_t *data,
- quadlet_t arg);
-struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host,
- nodeid_t node, u64 addr, int extcode,
- octlet_t *data, octlet_t arg);
-struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data);
-struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host,
- nodeid_t node, u64 addr,
- quadlet_t *buffer, size_t length);
-struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer,
- int length, int channel, int tag,
- int sync);
-int hpsb_packet_success(struct hpsb_packet *packet);
-int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
- u64 addr, quadlet_t *buffer, size_t length);
-int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
- u64 addr, quadlet_t *buffer, size_t length);
-int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation,
- u64 addr, int extcode, quadlet_t *data, quadlet_t arg);
-
-#ifdef HPSB_DEBUG_TLABELS
-extern spinlock_t hpsb_tlabel_lock;
-#endif
-
-#endif /* _IEEE1394_TRANSACTIONS_H */
diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h
deleted file mode 100644
index 9803aaa15be0..000000000000
--- a/drivers/ieee1394/ieee1394_types.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef _IEEE1394_TYPES_H
-#define _IEEE1394_TYPES_H
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <asm/byteorder.h>
-
-typedef u32 quadlet_t;
-typedef u64 octlet_t;
-typedef u16 nodeid_t;
-
-typedef u8 byte_t;
-typedef u64 nodeaddr_t;
-typedef u16 arm_length_t;
-
-#define BUS_MASK 0xffc0
-#define BUS_SHIFT 6
-#define NODE_MASK 0x003f
-#define LOCAL_BUS 0xffc0
-#define ALL_NODES 0x003f
-
-#define NODEID_TO_BUS(nodeid) ((nodeid & BUS_MASK) >> BUS_SHIFT)
-#define NODEID_TO_NODE(nodeid) (nodeid & NODE_MASK)
-
-/* Can be used to consistently print a node/bus ID. */
-#define NODE_BUS_FMT "%d-%02d:%04d"
-#define NODE_BUS_ARGS(__host, __nodeid) \
- __host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid)
-
-#define HPSB_PRINT(level, fmt, args...) \
- printk(level "ieee1394: " fmt "\n" , ## args)
-
-#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
-#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args)
-#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args)
-#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args)
-#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args)
-
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
-#define HPSB_DEBUG_TLABELS
-#else
-#define HPSB_VERBOSE(fmt, args...) do {} while (0)
-#endif
-
-#ifdef __BIG_ENDIAN
-
-static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count)
-{
- void *tmp = dest;
- u32 *src = (u32 *)__src;
-
- count /= 4;
- while (count--)
- *dest++ = swab32p(src++);
- return tmp;
-}
-
-#else
-
-static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count)
-{
- return memcpy(dest, src, count);
-}
-
-#endif /* __BIG_ENDIAN */
-
-#endif /* _IEEE1394_TYPES_H */
diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c
deleted file mode 100644
index 1cf6487b65ba..000000000000
--- a/drivers/ieee1394/iso.c
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * kernel ISO transmission/reception
- *
- * Copyright (C) 2002 Maas Digital LLC
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/pci.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-
-#include "hosts.h"
-#include "iso.h"
-
-/**
- * hpsb_iso_stop - stop DMA
- */
-void hpsb_iso_stop(struct hpsb_iso *iso)
-{
- if (!(iso->flags & HPSB_ISO_DRIVER_STARTED))
- return;
-
- iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
- XMIT_STOP : RECV_STOP, 0);
- iso->flags &= ~HPSB_ISO_DRIVER_STARTED;
-}
-
-/**
- * hpsb_iso_shutdown - deallocate buffer and DMA context
- */
-void hpsb_iso_shutdown(struct hpsb_iso *iso)
-{
- if (iso->flags & HPSB_ISO_DRIVER_INIT) {
- hpsb_iso_stop(iso);
- iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
- XMIT_SHUTDOWN : RECV_SHUTDOWN, 0);
- iso->flags &= ~HPSB_ISO_DRIVER_INIT;
- }
-
- dma_region_free(&iso->data_buf);
- kfree(iso);
-}
-
-static struct hpsb_iso *hpsb_iso_common_init(struct hpsb_host *host,
- enum hpsb_iso_type type,
- unsigned int data_buf_size,
- unsigned int buf_packets,
- int channel, int dma_mode,
- int irq_interval,
- void (*callback) (struct hpsb_iso
- *))
-{
- struct hpsb_iso *iso;
- int dma_direction;
-
- /* make sure driver supports the ISO API */
- if (!host->driver->isoctl) {
- printk(KERN_INFO
- "ieee1394: host driver '%s' does not support the rawiso API\n",
- host->driver->name);
- return NULL;
- }
-
- /* sanitize parameters */
-
- if (buf_packets < 2)
- buf_packets = 2;
-
- if ((dma_mode < HPSB_ISO_DMA_DEFAULT)
- || (dma_mode > HPSB_ISO_DMA_PACKET_PER_BUFFER))
- dma_mode = HPSB_ISO_DMA_DEFAULT;
-
- if ((irq_interval < 0) || (irq_interval > buf_packets / 4))
- irq_interval = buf_packets / 4;
- if (irq_interval == 0) /* really interrupt for each packet */
- irq_interval = 1;
-
- if (channel < -1 || channel >= 64)
- return NULL;
-
- /* channel = -1 is OK for multi-channel recv but not for xmit */
- if (type == HPSB_ISO_XMIT && channel < 0)
- return NULL;
-
- /* allocate and write the struct hpsb_iso */
-
- iso =
- kmalloc(sizeof(*iso) +
- buf_packets * sizeof(struct hpsb_iso_packet_info),
- GFP_KERNEL);
- if (!iso)
- return NULL;
-
- iso->infos = (struct hpsb_iso_packet_info *)(iso + 1);
-
- iso->type = type;
- iso->host = host;
- iso->hostdata = NULL;
- iso->callback = callback;
- init_waitqueue_head(&iso->waitq);
- iso->channel = channel;
- iso->irq_interval = irq_interval;
- iso->dma_mode = dma_mode;
- dma_region_init(&iso->data_buf);
- iso->buf_size = PAGE_ALIGN(data_buf_size);
- iso->buf_packets = buf_packets;
- iso->pkt_dma = 0;
- iso->first_packet = 0;
- spin_lock_init(&iso->lock);
-
- if (iso->type == HPSB_ISO_XMIT) {
- iso->n_ready_packets = iso->buf_packets;
- dma_direction = PCI_DMA_TODEVICE;
- } else {
- iso->n_ready_packets = 0;
- dma_direction = PCI_DMA_FROMDEVICE;
- }
-
- atomic_set(&iso->overflows, 0);
- iso->bytes_discarded = 0;
- iso->flags = 0;
- iso->prebuffer = 0;
-
- /* allocate the packet buffer */
- if (dma_region_alloc
- (&iso->data_buf, iso->buf_size, host->pdev, dma_direction))
- goto err;
-
- return iso;
-
- err:
- hpsb_iso_shutdown(iso);
- return NULL;
-}
-
-/**
- * hpsb_iso_n_ready - returns number of packets ready to send or receive
- */
-int hpsb_iso_n_ready(struct hpsb_iso *iso)
-{
- unsigned long flags;
- int val;
-
- spin_lock_irqsave(&iso->lock, flags);
- val = iso->n_ready_packets;
- spin_unlock_irqrestore(&iso->lock, flags);
-
- return val;
-}
-
-/**
- * hpsb_iso_xmit_init - allocate the buffer and DMA context
- */
-struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host,
- unsigned int data_buf_size,
- unsigned int buf_packets,
- int channel,
- int speed,
- int irq_interval,
- void (*callback) (struct hpsb_iso *))
-{
- struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT,
- data_buf_size, buf_packets,
- channel,
- HPSB_ISO_DMA_DEFAULT,
- irq_interval, callback);
- if (!iso)
- return NULL;
-
- iso->speed = speed;
-
- /* tell the driver to start working */
- if (host->driver->isoctl(iso, XMIT_INIT, 0))
- goto err;
-
- iso->flags |= HPSB_ISO_DRIVER_INIT;
- return iso;
-
- err:
- hpsb_iso_shutdown(iso);
- return NULL;
-}
-
-/**
- * hpsb_iso_recv_init - allocate the buffer and DMA context
- *
- * Note, if channel = -1, multi-channel receive is enabled.
- */
-struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host,
- unsigned int data_buf_size,
- unsigned int buf_packets,
- int channel,
- int dma_mode,
- int irq_interval,
- void (*callback) (struct hpsb_iso *))
-{
- struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV,
- data_buf_size, buf_packets,
- channel, dma_mode,
- irq_interval, callback);
- if (!iso)
- return NULL;
-
- /* tell the driver to start working */
- if (host->driver->isoctl(iso, RECV_INIT, 0))
- goto err;
-
- iso->flags |= HPSB_ISO_DRIVER_INIT;
- return iso;
-
- err:
- hpsb_iso_shutdown(iso);
- return NULL;
-}
-
-/**
- * hpsb_iso_recv_listen_channel
- *
- * multi-channel only
- */
-int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel)
-{
- if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
- return -EINVAL;
- return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel);
-}
-
-/**
- * hpsb_iso_recv_unlisten_channel
- *
- * multi-channel only
- */
-int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel)
-{
- if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
- return -EINVAL;
- return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel);
-}
-
-/**
- * hpsb_iso_recv_set_channel_mask
- *
- * multi-channel only
- */
-int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
-{
- if (iso->type != HPSB_ISO_RECV || iso->channel != -1)
- return -EINVAL;
- return iso->host->driver->isoctl(iso, RECV_SET_CHANNEL_MASK,
- (unsigned long)&mask);
-}
-
-/**
- * hpsb_iso_recv_flush - check for arrival of new packets
- *
- * check for arrival of new packets immediately (even if irq_interval
- * has not yet been reached)
- */
-int hpsb_iso_recv_flush(struct hpsb_iso *iso)
-{
- if (iso->type != HPSB_ISO_RECV)
- return -EINVAL;
- return iso->host->driver->isoctl(iso, RECV_FLUSH, 0);
-}
-
-static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle)
-{
- int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle);
- if (retval)
- return retval;
-
- iso->flags |= HPSB_ISO_DRIVER_STARTED;
- return retval;
-}
-
-/**
- * hpsb_iso_xmit_start - start DMA
- */
-int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer)
-{
- if (iso->type != HPSB_ISO_XMIT)
- return -1;
-
- if (iso->flags & HPSB_ISO_DRIVER_STARTED)
- return 0;
-
- if (cycle < -1)
- cycle = -1;
- else if (cycle >= 8000)
- cycle %= 8000;
-
- iso->xmit_cycle = cycle;
-
- if (prebuffer < 0)
- prebuffer = iso->buf_packets - 1;
- else if (prebuffer == 0)
- prebuffer = 1;
-
- if (prebuffer >= iso->buf_packets)
- prebuffer = iso->buf_packets - 1;
-
- iso->prebuffer = prebuffer;
-
- /* remember the starting cycle; DMA will commence from xmit_queue_packets()
- once enough packets have been buffered */
- iso->start_cycle = cycle;
-
- return 0;
-}
-
-/**
- * hpsb_iso_recv_start - start DMA
- */
-int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
-{
- int retval = 0;
- int isoctl_args[3];
-
- if (iso->type != HPSB_ISO_RECV)
- return -1;
-
- if (iso->flags & HPSB_ISO_DRIVER_STARTED)
- return 0;
-
- if (cycle < -1)
- cycle = -1;
- else if (cycle >= 8000)
- cycle %= 8000;
-
- isoctl_args[0] = cycle;
-
- if (tag_mask < 0)
- /* match all tags */
- tag_mask = 0xF;
- isoctl_args[1] = tag_mask;
-
- isoctl_args[2] = sync;
-
- retval =
- iso->host->driver->isoctl(iso, RECV_START,
- (unsigned long)&isoctl_args[0]);
- if (retval)
- return retval;
-
- iso->flags |= HPSB_ISO_DRIVER_STARTED;
- return retval;
-}
-
-/* check to make sure the user has not supplied bogus values of offset/len
- * that would cause the kernel to access memory outside the buffer */
-static int hpsb_iso_check_offset_len(struct hpsb_iso *iso,
- unsigned int offset, unsigned short len,
- unsigned int *out_offset,
- unsigned short *out_len)
-{
- if (offset >= iso->buf_size)
- return -EFAULT;
-
- /* make sure the packet does not go beyond the end of the buffer */
- if (offset + len > iso->buf_size)
- return -EFAULT;
-
- /* check for wrap-around */
- if (offset + len < offset)
- return -EFAULT;
-
- /* now we can trust 'offset' and 'length' */
- *out_offset = offset;
- *out_len = len;
-
- return 0;
-}
-
-/**
- * hpsb_iso_xmit_queue_packet - queue a packet for transmission.
- *
- * @offset is relative to the beginning of the DMA buffer, where the packet's
- * data payload should already have been placed.
- */
-int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
- u8 tag, u8 sy)
-{
- struct hpsb_iso_packet_info *info;
- unsigned long flags;
- int rv;
-
- if (iso->type != HPSB_ISO_XMIT)
- return -EINVAL;
-
- /* is there space in the buffer? */
- if (iso->n_ready_packets <= 0) {
- return -EBUSY;
- }
-
- info = &iso->infos[iso->first_packet];
-
- /* check for bogus offset/length */
- if (hpsb_iso_check_offset_len
- (iso, offset, len, &info->offset, &info->len))
- return -EFAULT;
-
- info->tag = tag;
- info->sy = sy;
-
- spin_lock_irqsave(&iso->lock, flags);
-
- rv = iso->host->driver->isoctl(iso, XMIT_QUEUE, (unsigned long)info);
- if (rv)
- goto out;
-
- /* increment cursors */
- iso->first_packet = (iso->first_packet + 1) % iso->buf_packets;
- iso->xmit_cycle = (iso->xmit_cycle + 1) % 8000;
- iso->n_ready_packets--;
-
- if (iso->prebuffer != 0) {
- iso->prebuffer--;
- if (iso->prebuffer <= 0) {
- iso->prebuffer = 0;
- rv = do_iso_xmit_start(iso, iso->start_cycle);
- }
- }
-
- out:
- spin_unlock_irqrestore(&iso->lock, flags);
- return rv;
-}
-
-/**
- * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted
- */
-int hpsb_iso_xmit_sync(struct hpsb_iso *iso)
-{
- if (iso->type != HPSB_ISO_XMIT)
- return -EINVAL;
-
- return wait_event_interruptible(iso->waitq,
- hpsb_iso_n_ready(iso) ==
- iso->buf_packets);
-}
-
-/**
- * hpsb_iso_packet_sent
- *
- * Available to low-level drivers.
- *
- * Call after a packet has been transmitted to the bus (interrupt context is
- * OK). @cycle is the _exact_ cycle the packet was sent on. @error should be
- * non-zero if some sort of error occurred when sending the packet.
- */
-void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error)
-{
- unsigned long flags;
- spin_lock_irqsave(&iso->lock, flags);
-
- /* predict the cycle of the next packet to be queued */
-
- /* jump ahead by the number of packets that are already buffered */
- cycle += iso->buf_packets - iso->n_ready_packets;
- cycle %= 8000;
-
- iso->xmit_cycle = cycle;
- iso->n_ready_packets++;
- iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets;
-
- if (iso->n_ready_packets == iso->buf_packets || error != 0) {
- /* the buffer has run empty! */
- atomic_inc(&iso->overflows);
- }
-
- spin_unlock_irqrestore(&iso->lock, flags);
-}
-
-/**
- * hpsb_iso_packet_received
- *
- * Available to low-level drivers.
- *
- * Call after a packet has been received (interrupt context is OK).
- */
-void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
- u16 total_len, u16 cycle, u8 channel, u8 tag,
- u8 sy)
-{
- unsigned long flags;
- spin_lock_irqsave(&iso->lock, flags);
-
- if (iso->n_ready_packets == iso->buf_packets) {
- /* overflow! */
- atomic_inc(&iso->overflows);
- /* Record size of this discarded packet */
- iso->bytes_discarded += total_len;
- } else {
- struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma];
- info->offset = offset;
- info->len = len;
- info->total_len = total_len;
- info->cycle = cycle;
- info->channel = channel;
- info->tag = tag;
- info->sy = sy;
-
- iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets;
- iso->n_ready_packets++;
- }
-
- spin_unlock_irqrestore(&iso->lock, flags);
-}
-
-/**
- * hpsb_iso_recv_release_packets - release packets, reuse buffer
- *
- * @n_packets have been read out of the buffer, re-use the buffer space
- */
-int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets)
-{
- unsigned long flags;
- unsigned int i;
- int rv = 0;
-
- if (iso->type != HPSB_ISO_RECV)
- return -1;
-
- spin_lock_irqsave(&iso->lock, flags);
- for (i = 0; i < n_packets; i++) {
- rv = iso->host->driver->isoctl(iso, RECV_RELEASE,
- (unsigned long)&iso->infos[iso->
- first_packet]);
- if (rv)
- break;
-
- iso->first_packet = (iso->first_packet + 1) % iso->buf_packets;
- iso->n_ready_packets--;
-
- /* release memory from packets discarded when queue was full */
- if (iso->n_ready_packets == 0) { /* Release only after all prior packets handled */
- if (iso->bytes_discarded != 0) {
- struct hpsb_iso_packet_info inf;
- inf.total_len = iso->bytes_discarded;
- iso->host->driver->isoctl(iso, RECV_RELEASE,
- (unsigned long)&inf);
- iso->bytes_discarded = 0;
- }
- }
- }
- spin_unlock_irqrestore(&iso->lock, flags);
- return rv;
-}
-
-/**
- * hpsb_iso_wake
- *
- * Available to low-level drivers.
- *
- * Call to wake waiting processes after buffer space has opened up.
- */
-void hpsb_iso_wake(struct hpsb_iso *iso)
-{
- wake_up_interruptible(&iso->waitq);
-
- if (iso->callback)
- iso->callback(iso);
-}
diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h
deleted file mode 100644
index c2089c093aa7..000000000000
--- a/drivers/ieee1394/iso.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * kernel ISO transmission/reception
- *
- * Copyright (C) 2002 Maas Digital LLC
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#ifndef IEEE1394_ISO_H
-#define IEEE1394_ISO_H
-
-#include <linux/spinlock_types.h>
-#include <linux/wait.h>
-#include <asm/atomic.h>
-#include <asm/types.h>
-
-#include "dma.h"
-
-struct hpsb_host;
-
-/* high-level ISO interface */
-
-/*
- * This API sends and receives isochronous packets on a large,
- * virtually-contiguous kernel memory buffer. The buffer may be mapped
- * into a user-space process for zero-copy transmission and reception.
- *
- * There are no explicit boundaries between packets in the buffer. A
- * packet may be transmitted or received at any location. However,
- * low-level drivers may impose certain restrictions on alignment or
- * size of packets. (e.g. in OHCI no packet may cross a page boundary,
- * and packets should be quadlet-aligned)
- */
-
-/* Packet descriptor - the API maintains a ring buffer of these packet
- * descriptors in kernel memory (hpsb_iso.infos[]). */
-struct hpsb_iso_packet_info {
- /* offset of data payload relative to the first byte of the buffer */
- __u32 offset;
-
- /* length of the data payload, in bytes (not including the isochronous
- * header) */
- __u16 len;
-
- /* (recv only) the cycle number (mod 8000) on which the packet was
- * received */
- __u16 cycle;
-
- /* (recv only) channel on which the packet was received */
- __u8 channel;
-
- /* 2-bit 'tag' and 4-bit 'sy' fields of the isochronous header */
- __u8 tag;
- __u8 sy;
-
- /* length in bytes of the packet including header/trailer.
- * MUST be at structure end, since the first part of this structure is
- * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is
- * copied to userspace and is accessed there through libraw1394. */
- __u16 total_len;
-};
-
-enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 };
-
-/* The mode of the dma when receiving iso data. Must be supported by chip */
-enum raw1394_iso_dma_recv_mode {
- HPSB_ISO_DMA_DEFAULT = -1,
- HPSB_ISO_DMA_OLD_ABI = 0,
- HPSB_ISO_DMA_BUFFERFILL = 1,
- HPSB_ISO_DMA_PACKET_PER_BUFFER = 2
-};
-
-struct hpsb_iso {
- enum hpsb_iso_type type;
-
- /* pointer to low-level driver and its private data */
- struct hpsb_host *host;
- void *hostdata;
-
- /* a function to be called (from interrupt context) after
- * outgoing packets have been sent, or incoming packets have
- * arrived */
- void (*callback)(struct hpsb_iso*);
-
- /* wait for buffer space */
- wait_queue_head_t waitq;
-
- int speed; /* IEEE1394_SPEED_100, 200, or 400 */
- int channel; /* -1 if multichannel */
- int dma_mode; /* dma receive mode */
-
-
- /* greatest # of packets between interrupts - controls
- * the maximum latency of the buffer */
- int irq_interval;
-
- /* the buffer for packet data payloads */
- struct dma_region data_buf;
-
- /* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */
- unsigned int buf_size;
-
- /* # of packets in the ringbuffer */
- unsigned int buf_packets;
-
- /* protects packet cursors */
- spinlock_t lock;
-
- /* the index of the next packet that will be produced
- or consumed by the user */
- int first_packet;
-
- /* the index of the next packet that will be transmitted
- or received by the 1394 hardware */
- int pkt_dma;
-
- /* how many packets, starting at first_packet:
- * (transmit) are ready to be filled with data
- * (receive) contain received data */
- int n_ready_packets;
-
- /* how many times the buffer has overflowed or underflowed */
- atomic_t overflows;
- /* how many cycles were skipped for a given context */
- atomic_t skips;
-
- /* Current number of bytes lost in discarded packets */
- int bytes_discarded;
-
- /* private flags to track initialization progress */
-#define HPSB_ISO_DRIVER_INIT (1<<0)
-#define HPSB_ISO_DRIVER_STARTED (1<<1)
- unsigned int flags;
-
- /* # of packets left to prebuffer (xmit only) */
- int prebuffer;
-
- /* starting cycle for DMA (xmit only) */
- int start_cycle;
-
- /* cycle at which next packet will be transmitted,
- * -1 if not known */
- int xmit_cycle;
-
- /* ringbuffer of packet descriptors in regular kernel memory
- * XXX Keep this last, since we use over-allocated memory from
- * this entry to fill this field. */
- struct hpsb_iso_packet_info *infos;
-};
-
-/* functions available to high-level drivers (e.g. raw1394) */
-
-struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host,
- unsigned int data_buf_size,
- unsigned int buf_packets,
- int channel,
- int speed,
- int irq_interval,
- void (*callback)(struct hpsb_iso*));
-struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host,
- unsigned int data_buf_size,
- unsigned int buf_packets,
- int channel,
- int dma_mode,
- int irq_interval,
- void (*callback)(struct hpsb_iso*));
-int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel);
-int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel);
-int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask);
-int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle,
- int prebuffer);
-int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle,
- int tag_mask, int sync);
-void hpsb_iso_stop(struct hpsb_iso *iso);
-void hpsb_iso_shutdown(struct hpsb_iso *iso);
-int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
- u8 tag, u8 sy);
-int hpsb_iso_xmit_sync(struct hpsb_iso *iso);
-int hpsb_iso_recv_release_packets(struct hpsb_iso *recv,
- unsigned int n_packets);
-int hpsb_iso_recv_flush(struct hpsb_iso *iso);
-int hpsb_iso_n_ready(struct hpsb_iso *iso);
-
-/* the following are callbacks available to low-level drivers */
-
-void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error);
-void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
- u16 total_len, u16 cycle, u8 channel, u8 tag,
- u8 sy);
-void hpsb_iso_wake(struct hpsb_iso *iso);
-
-#endif /* IEEE1394_ISO_H */
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
deleted file mode 100644
index 18350213479e..000000000000
--- a/drivers/ieee1394/nodemgr.c
+++ /dev/null
@@ -1,1901 +0,0 @@
-/*
- * Node information (ConfigROM) collection and management.
- *
- * Copyright (C) 2000 Andreas E. Bombe
- * 2001-2003 Ben Collins <bcollins@debian.net>
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/bitmap.h>
-#include <linux/kernel.h>
-#include <linux/kmemcheck.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
-#include <linux/freezer.h>
-#include <asm/atomic.h>
-
-#include "csr.h"
-#include "highlevel.h"
-#include "hosts.h"
-#include "ieee1394.h"
-#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
-#include "ieee1394_types.h"
-#include "ieee1394_transactions.h"
-#include "nodemgr.h"
-
-static int ignore_drivers;
-module_param(ignore_drivers, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers.");
-
-struct nodemgr_csr_info {
- struct hpsb_host *host;
- nodeid_t nodeid;
- unsigned int generation;
-
- kmemcheck_bitfield_begin(flags);
- unsigned int speed_unverified:1;
- kmemcheck_bitfield_end(flags);
-};
-
-
-/*
- * Correct the speed map entry. This is necessary
- * - for nodes with link speed < phy speed,
- * - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX.
- * A possible speed is determined by trial and error, using quadlet reads.
- */
-static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr,
- quadlet_t *buffer)
-{
- quadlet_t q;
- u8 i, *speed, old_speed, good_speed;
- int error;
-
- speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]);
- old_speed = *speed;
- good_speed = IEEE1394_SPEED_MAX + 1;
-
- /* Try every speed from S100 to old_speed.
- * If we did it the other way around, a too low speed could be caught
- * if the retry succeeded for some other reason, e.g. because the link
- * just finished its initialization. */
- for (i = IEEE1394_SPEED_100; i <= old_speed; i++) {
- *speed = i;
- error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
- &q, 4);
- if (error)
- break;
- *buffer = q;
- good_speed = i;
- }
- if (good_speed <= IEEE1394_SPEED_MAX) {
- HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s",
- NODE_BUS_ARGS(ci->host, ci->nodeid),
- hpsb_speedto_str[good_speed]);
- *speed = good_speed;
- ci->speed_unverified = 0;
- return 0;
- }
- *speed = old_speed;
- return error;
-}
-
-static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr,
- void *buffer, void *__ci)
-{
- struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci;
- int i, error;
-
- for (i = 1; ; i++) {
- error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
- buffer, 4);
- if (!error) {
- ci->speed_unverified = 0;
- break;
- }
- /* Give up after 3rd failure. */
- if (i == 3)
- break;
-
- /* The ieee1394_core guessed the node's speed capability from
- * the self ID. Check whether a lower speed works. */
- if (ci->speed_unverified) {
- error = nodemgr_check_speed(ci, addr, buffer);
- if (!error)
- break;
- }
- if (msleep_interruptible(334))
- return -EINTR;
- }
- return error;
-}
-
-static struct csr1212_bus_ops nodemgr_csr_ops = {
- .bus_read = nodemgr_bus_read,
-};
-
-
-/*
- * Basically what we do here is start off retrieving the bus_info block.
- * From there will fill in some info about the node, verify it is of IEEE
- * 1394 type, and that the crc checks out ok. After that we start off with
- * the root directory, and subdirectories. To do this, we retrieve the
- * quadlet header for a directory, find out the length, and retrieve the
- * complete directory entry (be it a leaf or a directory). We then process
- * it and add the info to our structure for that particular node.
- *
- * We verify CRC's along the way for each directory/block/leaf. The entire
- * node structure is generic, and simply stores the information in a way
- * that's easy to parse by the protocol interface.
- */
-
-/*
- * The nodemgr relies heavily on the Driver Model for device callbacks and
- * driver/device mappings. The old nodemgr used to handle all this itself,
- * but now we are much simpler because of the LDM.
- */
-
-struct host_info {
- struct hpsb_host *host;
- struct list_head list;
- struct task_struct *thread;
-};
-
-static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
-static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env);
-
-struct bus_type ieee1394_bus_type = {
- .name = "ieee1394",
- .match = nodemgr_bus_match,
-};
-
-static void host_cls_release(struct device *dev)
-{
- put_device(&container_of((dev), struct hpsb_host, host_dev)->device);
-}
-
-struct class hpsb_host_class = {
- .name = "ieee1394_host",
- .dev_release = host_cls_release,
-};
-
-static void ne_cls_release(struct device *dev)
-{
- put_device(&container_of((dev), struct node_entry, node_dev)->device);
-}
-
-static struct class nodemgr_ne_class = {
- .name = "ieee1394_node",
- .dev_release = ne_cls_release,
-};
-
-static void ud_cls_release(struct device *dev)
-{
- put_device(&container_of((dev), struct unit_directory, unit_dev)->device);
-}
-
-/* The name here is only so that unit directory hotplug works with old
- * style hotplug, which only ever did unit directories anyway.
- */
-static struct class nodemgr_ud_class = {
- .name = "ieee1394",
- .dev_release = ud_cls_release,
- .dev_uevent = nodemgr_uevent,
-};
-
-static struct hpsb_highlevel nodemgr_highlevel;
-
-
-static void nodemgr_release_ud(struct device *dev)
-{
- struct unit_directory *ud = container_of(dev, struct unit_directory, device);
-
- if (ud->vendor_name_kv)
- csr1212_release_keyval(ud->vendor_name_kv);
- if (ud->model_name_kv)
- csr1212_release_keyval(ud->model_name_kv);
-
- kfree(ud);
-}
-
-static void nodemgr_release_ne(struct device *dev)
-{
- struct node_entry *ne = container_of(dev, struct node_entry, device);
-
- if (ne->vendor_name_kv)
- csr1212_release_keyval(ne->vendor_name_kv);
-
- kfree(ne);
-}
-
-
-static void nodemgr_release_host(struct device *dev)
-{
- struct hpsb_host *host = container_of(dev, struct hpsb_host, device);
-
- csr1212_destroy_csr(host->csr.rom);
-
- kfree(host);
-}
-
-static int nodemgr_ud_platform_data;
-
-static struct device nodemgr_dev_template_ud = {
- .bus = &ieee1394_bus_type,
- .release = nodemgr_release_ud,
- .platform_data = &nodemgr_ud_platform_data,
-};
-
-static struct device nodemgr_dev_template_ne = {
- .bus = &ieee1394_bus_type,
- .release = nodemgr_release_ne,
-};
-
-/* This dummy driver prevents the host devices from being scanned. We have no
- * useful drivers for them yet, and there would be a deadlock possible if the
- * driver core scans the host device while the host's low-level driver (i.e.
- * the host's parent device) is being removed. */
-static struct device_driver nodemgr_mid_layer_driver = {
- .bus = &ieee1394_bus_type,
- .name = "nodemgr",
- .owner = THIS_MODULE,
-};
-
-struct device nodemgr_dev_template_host = {
- .bus = &ieee1394_bus_type,
- .release = nodemgr_release_host,
-};
-
-
-#define fw_attr(class, class_type, field, type, format_string) \
-static ssize_t fw_show_##class##_##field (struct device *dev, struct device_attribute *attr, char *buf)\
-{ \
- class_type *class; \
- class = container_of(dev, class_type, device); \
- return sprintf(buf, format_string, (type)class->field); \
-} \
-static struct device_attribute dev_attr_##class##_##field = { \
- .attr = {.name = __stringify(field), .mode = S_IRUGO }, \
- .show = fw_show_##class##_##field, \
-};
-
-#define fw_attr_td(class, class_type, td_kv) \
-static ssize_t fw_show_##class##_##td_kv (struct device *dev, struct device_attribute *attr, char *buf)\
-{ \
- int len; \
- class_type *class = container_of(dev, class_type, device); \
- len = (class->td_kv->value.leaf.len - 2) * sizeof(quadlet_t); \
- memcpy(buf, \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(class->td_kv), \
- len); \
- while (buf[len - 1] == '\0') \
- len--; \
- buf[len++] = '\n'; \
- buf[len] = '\0'; \
- return len; \
-} \
-static struct device_attribute dev_attr_##class##_##td_kv = { \
- .attr = {.name = __stringify(td_kv), .mode = S_IRUGO }, \
- .show = fw_show_##class##_##td_kv, \
-};
-
-
-#define fw_drv_attr(field, type, format_string) \
-static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \
-{ \
- struct hpsb_protocol_driver *driver; \
- driver = container_of(drv, struct hpsb_protocol_driver, driver); \
- return sprintf(buf, format_string, (type)driver->field);\
-} \
-static struct driver_attribute driver_attr_drv_##field = { \
- .attr = {.name = __stringify(field), .mode = S_IRUGO }, \
- .show = fw_drv_show_##field, \
-};
-
-
-static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct node_entry *ne = container_of(dev, struct node_entry, device);
-
- return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) "
- "LSPD(%d) MAX_REC(%d) MAX_ROM(%d) CYC_CLK_ACC(%d)\n",
- ne->busopt.irmc,
- ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
- ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd,
- ne->busopt.max_rec,
- ne->busopt.max_rom,
- ne->busopt.cyc_clk_acc);
-}
-static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
-
-
-#ifdef HPSB_DEBUG_TLABELS
-static ssize_t fw_show_ne_tlabels_free(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct node_entry *ne = container_of(dev, struct node_entry, device);
- unsigned long flags;
- unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
- int tf;
-
- spin_lock_irqsave(&hpsb_tlabel_lock, flags);
- tf = 64 - bitmap_weight(tp, 64);
- spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
-
- return sprintf(buf, "%d\n", tf);
-}
-static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
-
-
-static ssize_t fw_show_ne_tlabels_mask(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct node_entry *ne = container_of(dev, struct node_entry, device);
- unsigned long flags;
- unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
- u64 tm;
-
- spin_lock_irqsave(&hpsb_tlabel_lock, flags);
-#if (BITS_PER_LONG <= 32)
- tm = ((u64)tp[0] << 32) + tp[1];
-#else
- tm = tp[0];
-#endif
- spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
-
- return sprintf(buf, "0x%016llx\n", (unsigned long long)tm);
-}
-static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
-#endif /* HPSB_DEBUG_TLABELS */
-
-
-static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct unit_directory *ud = container_of(dev, struct unit_directory, device);
- int state = simple_strtoul(buf, NULL, 10);
-
- if (state == 1) {
- ud->ignore_driver = 1;
- device_release_driver(dev);
- } else if (state == 0)
- ud->ignore_driver = 0;
-
- return count;
-}
-static ssize_t fw_get_ignore_driver(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct unit_directory *ud = container_of(dev, struct unit_directory, device);
-
- return sprintf(buf, "%d\n", ud->ignore_driver);
-}
-static DEVICE_ATTR(ignore_driver, S_IWUSR | S_IRUGO, fw_get_ignore_driver, fw_set_ignore_driver);
-
-
-static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf,
- size_t count)
-{
- int error = 0;
-
- if (simple_strtoul(buf, NULL, 10) == 1)
- error = bus_rescan_devices(&ieee1394_bus_type);
- return error ? error : count;
-}
-static ssize_t fw_get_rescan(struct bus_type *bus, char *buf)
-{
- return sprintf(buf, "You can force a rescan of the bus for "
- "drivers by writing a 1 to this file\n");
-}
-static BUS_ATTR(rescan, S_IWUSR | S_IRUGO, fw_get_rescan, fw_set_rescan);
-
-
-static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size_t count)
-{
- int state = simple_strtoul(buf, NULL, 10);
-
- if (state == 1)
- ignore_drivers = 1;
- else if (state == 0)
- ignore_drivers = 0;
-
- return count;
-}
-static ssize_t fw_get_ignore_drivers(struct bus_type *bus, char *buf)
-{
- return sprintf(buf, "%d\n", ignore_drivers);
-}
-static BUS_ATTR(ignore_drivers, S_IWUSR | S_IRUGO, fw_get_ignore_drivers, fw_set_ignore_drivers);
-
-
-struct bus_attribute *const fw_bus_attrs[] = {
- &bus_attr_rescan,
- &bus_attr_ignore_drivers,
- NULL
-};
-
-
-fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n")
-fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n")
-
-fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n")
-fw_attr_td(ne, struct node_entry, vendor_name_kv)
-
-fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n")
-fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n")
-fw_attr(ne, struct node_entry, in_limbo, int, "%d\n");
-
-static struct device_attribute *const fw_ne_attrs[] = {
- &dev_attr_ne_guid,
- &dev_attr_ne_guid_vendor_id,
- &dev_attr_ne_capabilities,
- &dev_attr_ne_vendor_id,
- &dev_attr_ne_nodeid,
- &dev_attr_bus_options,
-#ifdef HPSB_DEBUG_TLABELS
- &dev_attr_tlabels_free,
- &dev_attr_tlabels_mask,
-#endif
-};
-
-
-
-fw_attr(ud, struct unit_directory, address, unsigned long long, "0x%016Lx\n")
-fw_attr(ud, struct unit_directory, length, int, "%d\n")
-/* These are all dependent on the value being provided */
-fw_attr(ud, struct unit_directory, vendor_id, unsigned int, "0x%06x\n")
-fw_attr(ud, struct unit_directory, model_id, unsigned int, "0x%06x\n")
-fw_attr(ud, struct unit_directory, specifier_id, unsigned int, "0x%06x\n")
-fw_attr(ud, struct unit_directory, version, unsigned int, "0x%06x\n")
-fw_attr_td(ud, struct unit_directory, vendor_name_kv)
-fw_attr_td(ud, struct unit_directory, model_name_kv)
-
-static struct device_attribute *const fw_ud_attrs[] = {
- &dev_attr_ud_address,
- &dev_attr_ud_length,
- &dev_attr_ignore_driver,
-};
-
-
-fw_attr(host, struct hpsb_host, node_count, int, "%d\n")
-fw_attr(host, struct hpsb_host, selfid_count, int, "%d\n")
-fw_attr(host, struct hpsb_host, nodes_active, int, "%d\n")
-fw_attr(host, struct hpsb_host, in_bus_reset, int, "%d\n")
-fw_attr(host, struct hpsb_host, is_root, int, "%d\n")
-fw_attr(host, struct hpsb_host, is_cycmst, int, "%d\n")
-fw_attr(host, struct hpsb_host, is_irm, int, "%d\n")
-fw_attr(host, struct hpsb_host, is_busmgr, int, "%d\n")
-
-static struct device_attribute *const fw_host_attrs[] = {
- &dev_attr_host_node_count,
- &dev_attr_host_selfid_count,
- &dev_attr_host_nodes_active,
- &dev_attr_host_in_bus_reset,
- &dev_attr_host_is_root,
- &dev_attr_host_is_cycmst,
- &dev_attr_host_is_irm,
- &dev_attr_host_is_busmgr,
-};
-
-
-static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf)
-{
- struct hpsb_protocol_driver *driver;
- const struct ieee1394_device_id *id;
- int length = 0;
- char *scratch = buf;
-
- driver = container_of(drv, struct hpsb_protocol_driver, driver);
- id = driver->id_table;
- if (!id)
- return 0;
-
- for (; id->match_flags != 0; id++) {
- int need_coma = 0;
-
- if (id->match_flags & IEEE1394_MATCH_VENDOR_ID) {
- length += sprintf(scratch, "vendor_id=0x%06x", id->vendor_id);
- scratch = buf + length;
- need_coma++;
- }
-
- if (id->match_flags & IEEE1394_MATCH_MODEL_ID) {
- length += sprintf(scratch, "%smodel_id=0x%06x",
- need_coma++ ? "," : "",
- id->model_id);
- scratch = buf + length;
- }
-
- if (id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) {
- length += sprintf(scratch, "%sspecifier_id=0x%06x",
- need_coma++ ? "," : "",
- id->specifier_id);
- scratch = buf + length;
- }
-
- if (id->match_flags & IEEE1394_MATCH_VERSION) {
- length += sprintf(scratch, "%sversion=0x%06x",
- need_coma++ ? "," : "",
- id->version);
- scratch = buf + length;
- }
-
- if (need_coma) {
- *scratch++ = '\n';
- length++;
- }
- }
-
- return length;
-}
-static DRIVER_ATTR(device_ids,S_IRUGO,fw_show_drv_device_ids,NULL);
-
-
-fw_drv_attr(name, const char *, "%s\n")
-
-static struct driver_attribute *const fw_drv_attrs[] = {
- &driver_attr_drv_name,
- &driver_attr_device_ids,
-};
-
-
-static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver)
-{
- struct device_driver *drv = &driver->driver;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++)
- if (driver_create_file(drv, fw_drv_attrs[i]))
- goto fail;
- return;
-fail:
- HPSB_ERR("Failed to add sysfs attribute");
-}
-
-
-static void nodemgr_remove_drv_files(struct hpsb_protocol_driver *driver)
-{
- struct device_driver *drv = &driver->driver;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++)
- driver_remove_file(drv, fw_drv_attrs[i]);
-}
-
-
-static void nodemgr_create_ne_dev_files(struct node_entry *ne)
-{
- struct device *dev = &ne->device;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++)
- if (device_create_file(dev, fw_ne_attrs[i]))
- goto fail;
- return;
-fail:
- HPSB_ERR("Failed to add sysfs attribute");
-}
-
-
-static void nodemgr_create_host_dev_files(struct hpsb_host *host)
-{
- struct device *dev = &host->device;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++)
- if (device_create_file(dev, fw_host_attrs[i]))
- goto fail;
- return;
-fail:
- HPSB_ERR("Failed to add sysfs attribute");
-}
-
-
-static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
- nodeid_t nodeid);
-
-static void nodemgr_update_host_dev_links(struct hpsb_host *host)
-{
- struct device *dev = &host->device;
- struct node_entry *ne;
-
- sysfs_remove_link(&dev->kobj, "irm_id");
- sysfs_remove_link(&dev->kobj, "busmgr_id");
- sysfs_remove_link(&dev->kobj, "host_id");
-
- if ((ne = find_entry_by_nodeid(host, host->irm_id)) &&
- sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id"))
- goto fail;
- if ((ne = find_entry_by_nodeid(host, host->busmgr_id)) &&
- sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id"))
- goto fail;
- if ((ne = find_entry_by_nodeid(host, host->node_id)) &&
- sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id"))
- goto fail;
- return;
-fail:
- HPSB_ERR("Failed to update sysfs attributes for host %d", host->id);
-}
-
-static void nodemgr_create_ud_dev_files(struct unit_directory *ud)
-{
- struct device *dev = &ud->device;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++)
- if (device_create_file(dev, fw_ud_attrs[i]))
- goto fail;
- if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
- if (device_create_file(dev, &dev_attr_ud_specifier_id))
- goto fail;
- if (ud->flags & UNIT_DIRECTORY_VERSION)
- if (device_create_file(dev, &dev_attr_ud_version))
- goto fail;
- if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) {
- if (device_create_file(dev, &dev_attr_ud_vendor_id))
- goto fail;
- if (ud->vendor_name_kv &&
- device_create_file(dev, &dev_attr_ud_vendor_name_kv))
- goto fail;
- }
- if (ud->flags & UNIT_DIRECTORY_MODEL_ID) {
- if (device_create_file(dev, &dev_attr_ud_model_id))
- goto fail;
- if (ud->model_name_kv &&
- device_create_file(dev, &dev_attr_ud_model_name_kv))
- goto fail;
- }
- return;
-fail:
- HPSB_ERR("Failed to add sysfs attribute");
-}
-
-
-static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
-{
- struct hpsb_protocol_driver *driver;
- struct unit_directory *ud;
- const struct ieee1394_device_id *id;
-
- /* We only match unit directories */
- if (dev->platform_data != &nodemgr_ud_platform_data)
- return 0;
-
- ud = container_of(dev, struct unit_directory, device);
- if (ud->ne->in_limbo || ud->ignore_driver)
- return 0;
-
- /* We only match drivers of type hpsb_protocol_driver */
- if (drv == &nodemgr_mid_layer_driver)
- return 0;
-
- driver = container_of(drv, struct hpsb_protocol_driver, driver);
- id = driver->id_table;
- if (!id)
- return 0;
-
- for (; id->match_flags != 0; id++) {
- if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
- id->vendor_id != ud->vendor_id)
- continue;
-
- if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) &&
- id->model_id != ud->model_id)
- continue;
-
- if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) &&
- id->specifier_id != ud->specifier_id)
- continue;
-
- if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
- id->version != ud->version)
- continue;
-
- return 1;
- }
-
- return 0;
-}
-
-
-static DEFINE_MUTEX(nodemgr_serialize_remove_uds);
-
-static int match_ne(struct device *dev, void *data)
-{
- struct unit_directory *ud;
- struct node_entry *ne = data;
-
- ud = container_of(dev, struct unit_directory, unit_dev);
- return ud->ne == ne;
-}
-
-static void nodemgr_remove_uds(struct node_entry *ne)
-{
- struct device *dev;
- struct unit_directory *ud;
-
- /* Use class_find device to iterate the devices. Since this code
- * may be called from other contexts besides the knodemgrds,
- * protect it by nodemgr_serialize_remove_uds.
- */
- mutex_lock(&nodemgr_serialize_remove_uds);
- for (;;) {
- dev = class_find_device(&nodemgr_ud_class, NULL, ne, match_ne);
- if (!dev)
- break;
- ud = container_of(dev, struct unit_directory, unit_dev);
- put_device(dev);
- device_unregister(&ud->unit_dev);
- device_unregister(&ud->device);
- }
- mutex_unlock(&nodemgr_serialize_remove_uds);
-}
-
-
-static void nodemgr_remove_ne(struct node_entry *ne)
-{
- struct device *dev;
-
- dev = get_device(&ne->device);
- if (!dev)
- return;
-
- HPSB_DEBUG("Node removed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
- NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
- nodemgr_remove_uds(ne);
-
- device_unregister(&ne->node_dev);
- device_unregister(dev);
-
- put_device(dev);
-}
-
-static int remove_host_dev(struct device *dev, void *data)
-{
- if (dev->bus == &ieee1394_bus_type)
- nodemgr_remove_ne(container_of(dev, struct node_entry,
- device));
- return 0;
-}
-
-static void nodemgr_remove_host_dev(struct device *dev)
-{
- device_for_each_child(dev, NULL, remove_host_dev);
- sysfs_remove_link(&dev->kobj, "irm_id");
- sysfs_remove_link(&dev->kobj, "busmgr_id");
- sysfs_remove_link(&dev->kobj, "host_id");
-}
-
-
-static void nodemgr_update_bus_options(struct node_entry *ne)
-{
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
- static const u16 mr[] = { 4, 64, 1024, 0};
-#endif
- quadlet_t busoptions = be32_to_cpu(ne->csr->bus_info_data[2]);
-
- ne->busopt.irmc = (busoptions >> 31) & 1;
- ne->busopt.cmc = (busoptions >> 30) & 1;
- ne->busopt.isc = (busoptions >> 29) & 1;
- ne->busopt.bmc = (busoptions >> 28) & 1;
- ne->busopt.pmc = (busoptions >> 27) & 1;
- ne->busopt.cyc_clk_acc = (busoptions >> 16) & 0xff;
- ne->busopt.max_rec = 1 << (((busoptions >> 12) & 0xf) + 1);
- ne->busopt.max_rom = (busoptions >> 8) & 0x3;
- ne->busopt.generation = (busoptions >> 4) & 0xf;
- ne->busopt.lnkspd = busoptions & 0x7;
-
- HPSB_VERBOSE("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d "
- "cyc_clk_acc=%d max_rec=%d max_rom=%d gen=%d lspd=%d",
- busoptions, ne->busopt.irmc, ne->busopt.cmc,
- ne->busopt.isc, ne->busopt.bmc, ne->busopt.pmc,
- ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
- mr[ne->busopt.max_rom],
- ne->busopt.generation, ne->busopt.lnkspd);
-}
-
-
-static struct node_entry *nodemgr_create_node(octlet_t guid,
- struct csr1212_csr *csr, struct hpsb_host *host,
- nodeid_t nodeid, unsigned int generation)
-{
- struct node_entry *ne;
-
- ne = kzalloc(sizeof(*ne), GFP_KERNEL);
- if (!ne)
- goto fail_alloc;
-
- ne->host = host;
- ne->nodeid = nodeid;
- ne->generation = generation;
- ne->needs_probe = true;
-
- ne->guid = guid;
- ne->guid_vendor_id = (guid >> 40) & 0xffffff;
- ne->csr = csr;
-
- memcpy(&ne->device, &nodemgr_dev_template_ne,
- sizeof(ne->device));
- ne->device.parent = &host->device;
- dev_set_name(&ne->device, "%016Lx", (unsigned long long)(ne->guid));
-
- ne->node_dev.parent = &ne->device;
- ne->node_dev.class = &nodemgr_ne_class;
- dev_set_name(&ne->node_dev, "%016Lx", (unsigned long long)(ne->guid));
-
- if (device_register(&ne->device))
- goto fail_devreg;
- if (device_register(&ne->node_dev))
- goto fail_classdevreg;
- get_device(&ne->device);
-
- nodemgr_create_ne_dev_files(ne);
-
- nodemgr_update_bus_options(ne);
-
- HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
- (host->node_id == nodeid) ? "Host" : "Node",
- NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid);
-
- return ne;
-
-fail_classdevreg:
- device_unregister(&ne->device);
-fail_devreg:
- kfree(ne);
-fail_alloc:
- HPSB_ERR("Failed to create node ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
- NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid);
-
- return NULL;
-}
-
-static int match_ne_guid(struct device *dev, void *data)
-{
- struct node_entry *ne;
- u64 *guid = data;
-
- ne = container_of(dev, struct node_entry, node_dev);
- return ne->guid == *guid;
-}
-
-static struct node_entry *find_entry_by_guid(u64 guid)
-{
- struct device *dev;
- struct node_entry *ne;
-
- dev = class_find_device(&nodemgr_ne_class, NULL, &guid, match_ne_guid);
- if (!dev)
- return NULL;
- ne = container_of(dev, struct node_entry, node_dev);
- put_device(dev);
-
- return ne;
-}
-
-struct match_nodeid_parameter {
- struct hpsb_host *host;
- nodeid_t nodeid;
-};
-
-static int match_ne_nodeid(struct device *dev, void *data)
-{
- int found = 0;
- struct node_entry *ne;
- struct match_nodeid_parameter *p = data;
-
- if (!dev)
- goto ret;
- ne = container_of(dev, struct node_entry, node_dev);
- if (ne->host == p->host && ne->nodeid == p->nodeid)
- found = 1;
-ret:
- return found;
-}
-
-static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
- nodeid_t nodeid)
-{
- struct device *dev;
- struct node_entry *ne;
- struct match_nodeid_parameter p;
-
- p.host = host;
- p.nodeid = nodeid;
-
- dev = class_find_device(&nodemgr_ne_class, NULL, &p, match_ne_nodeid);
- if (!dev)
- return NULL;
- ne = container_of(dev, struct node_entry, node_dev);
- put_device(dev);
-
- return ne;
-}
-
-
-static void nodemgr_register_device(struct node_entry *ne,
- struct unit_directory *ud, struct device *parent)
-{
- memcpy(&ud->device, &nodemgr_dev_template_ud,
- sizeof(ud->device));
-
- ud->device.parent = parent;
-
- dev_set_name(&ud->device, "%s-%u", dev_name(&ne->device), ud->id);
-
- ud->unit_dev.parent = &ud->device;
- ud->unit_dev.class = &nodemgr_ud_class;
- dev_set_name(&ud->unit_dev, "%s-%u", dev_name(&ne->device), ud->id);
-
- if (device_register(&ud->device))
- goto fail_devreg;
- if (device_register(&ud->unit_dev))
- goto fail_classdevreg;
- get_device(&ud->device);
-
- nodemgr_create_ud_dev_files(ud);
-
- return;
-
-fail_classdevreg:
- device_unregister(&ud->device);
-fail_devreg:
- HPSB_ERR("Failed to create unit %s", dev_name(&ud->device));
-}
-
-
-/* This implementation currently only scans the config rom and its
- * immediate unit directories looking for software_id and
- * software_version entries, in order to get driver autoloading working. */
-static struct unit_directory *nodemgr_process_unit_directory
- (struct node_entry *ne, struct csr1212_keyval *ud_kv,
- unsigned int *id, struct unit_directory *parent)
-{
- struct unit_directory *ud;
- struct unit_directory *ud_child = NULL;
- struct csr1212_dentry *dentry;
- struct csr1212_keyval *kv;
- u8 last_key_id = 0;
-
- ud = kzalloc(sizeof(*ud), GFP_KERNEL);
- if (!ud)
- goto unit_directory_error;
-
- ud->ne = ne;
- ud->ignore_driver = ignore_drivers;
- ud->address = ud_kv->offset + CSR1212_REGISTER_SPACE_BASE;
- ud->directory_id = ud->address & 0xffffff;
- ud->ud_kv = ud_kv;
- ud->id = (*id)++;
-
- /* inherit vendor_id from root directory if none exists in unit dir */
- ud->vendor_id = ne->vendor_id;
-
- csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) {
- switch (kv->key.id) {
- case CSR1212_KV_ID_VENDOR:
- if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
- ud->vendor_id = kv->value.immediate;
- ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
- }
- break;
-
- case CSR1212_KV_ID_MODEL:
- ud->model_id = kv->value.immediate;
- ud->flags |= UNIT_DIRECTORY_MODEL_ID;
- break;
-
- case CSR1212_KV_ID_SPECIFIER_ID:
- ud->specifier_id = kv->value.immediate;
- ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
- break;
-
- case CSR1212_KV_ID_VERSION:
- ud->version = kv->value.immediate;
- ud->flags |= UNIT_DIRECTORY_VERSION;
- break;
-
- case CSR1212_KV_ID_DESCRIPTOR:
- if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
- CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
- CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
- switch (last_key_id) {
- case CSR1212_KV_ID_VENDOR:
- csr1212_keep_keyval(kv);
- ud->vendor_name_kv = kv;
- break;
-
- case CSR1212_KV_ID_MODEL:
- csr1212_keep_keyval(kv);
- ud->model_name_kv = kv;
- break;
-
- }
- } /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */
- break;
-
- case CSR1212_KV_ID_DEPENDENT_INFO:
- /* Logical Unit Number */
- if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
- if (ud->flags & UNIT_DIRECTORY_HAS_LUN) {
- ud_child = kmemdup(ud, sizeof(*ud_child), GFP_KERNEL);
- if (!ud_child)
- goto unit_directory_error;
- nodemgr_register_device(ne, ud_child, &ne->device);
- ud_child = NULL;
-
- ud->id = (*id)++;
- }
- ud->lun = kv->value.immediate;
- ud->flags |= UNIT_DIRECTORY_HAS_LUN;
-
- /* Logical Unit Directory */
- } else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) {
- /* This should really be done in SBP2 as this is
- * doing SBP2 specific parsing.
- */
-
- /* first register the parent unit */
- ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY;
- if (ud->device.bus != &ieee1394_bus_type)
- nodemgr_register_device(ne, ud, &ne->device);
-
- /* process the child unit */
- ud_child = nodemgr_process_unit_directory(ne, kv, id, ud);
-
- if (ud_child == NULL)
- break;
-
- /* inherit unspecified values, the driver core picks it up */
- if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) &&
- !(ud_child->flags & UNIT_DIRECTORY_MODEL_ID))
- {
- ud_child->flags |= UNIT_DIRECTORY_MODEL_ID;
- ud_child->model_id = ud->model_id;
- }
- if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) &&
- !(ud_child->flags & UNIT_DIRECTORY_SPECIFIER_ID))
- {
- ud_child->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
- ud_child->specifier_id = ud->specifier_id;
- }
- if ((ud->flags & UNIT_DIRECTORY_VERSION) &&
- !(ud_child->flags & UNIT_DIRECTORY_VERSION))
- {
- ud_child->flags |= UNIT_DIRECTORY_VERSION;
- ud_child->version = ud->version;
- }
-
- /* register the child unit */
- ud_child->flags |= UNIT_DIRECTORY_LUN_DIRECTORY;
- nodemgr_register_device(ne, ud_child, &ud->device);
- }
-
- break;
-
- case CSR1212_KV_ID_DIRECTORY_ID:
- ud->directory_id = kv->value.immediate;
- break;
-
- default:
- break;
- }
- last_key_id = kv->key.id;
- }
-
- /* do not process child units here and only if not already registered */
- if (!parent && ud->device.bus != &ieee1394_bus_type)
- nodemgr_register_device(ne, ud, &ne->device);
-
- return ud;
-
-unit_directory_error:
- kfree(ud);
- return NULL;
-}
-
-
-static void nodemgr_process_root_directory(struct node_entry *ne)
-{
- unsigned int ud_id = 0;
- struct csr1212_dentry *dentry;
- struct csr1212_keyval *kv, *vendor_name_kv = NULL;
- u8 last_key_id = 0;
-
- ne->needs_probe = false;
-
- csr1212_for_each_dir_entry(ne->csr, kv, ne->csr->root_kv, dentry) {
- switch (kv->key.id) {
- case CSR1212_KV_ID_VENDOR:
- ne->vendor_id = kv->value.immediate;
- break;
-
- case CSR1212_KV_ID_NODE_CAPABILITIES:
- ne->capabilities = kv->value.immediate;
- break;
-
- case CSR1212_KV_ID_UNIT:
- nodemgr_process_unit_directory(ne, kv, &ud_id, NULL);
- break;
-
- case CSR1212_KV_ID_DESCRIPTOR:
- if (last_key_id == CSR1212_KV_ID_VENDOR) {
- if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
- CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
- CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
- csr1212_keep_keyval(kv);
- vendor_name_kv = kv;
- }
- }
- break;
- }
- last_key_id = kv->key.id;
- }
-
- if (ne->vendor_name_kv) {
- kv = ne->vendor_name_kv;
- ne->vendor_name_kv = vendor_name_kv;
- csr1212_release_keyval(kv);
- } else if (vendor_name_kv) {
- ne->vendor_name_kv = vendor_name_kv;
- if (device_create_file(&ne->device,
- &dev_attr_ne_vendor_name_kv) != 0)
- HPSB_ERR("Failed to add sysfs attribute");
- }
-}
-
-#ifdef CONFIG_HOTPLUG
-
-static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- struct unit_directory *ud;
- int retval = 0;
- /* ieee1394:venNmoNspNverN */
- char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
-
- if (!dev)
- return -ENODEV;
-
- ud = container_of(dev, struct unit_directory, unit_dev);
-
- if (ud->ne->in_limbo || ud->ignore_driver)
- return -ENODEV;
-
-#define PUT_ENVP(fmt,val) \
-do { \
- retval = add_uevent_var(env, fmt, val); \
- if (retval) \
- return retval; \
-} while (0)
-
- PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id);
- PUT_ENVP("MODEL_ID=%06x", ud->model_id);
- PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid);
- PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id);
- PUT_ENVP("VERSION=%06x", ud->version);
- snprintf(buf, sizeof(buf), "ieee1394:ven%08Xmo%08Xsp%08Xver%08X",
- ud->vendor_id,
- ud->model_id,
- ud->specifier_id,
- ud->version);
- PUT_ENVP("MODALIAS=%s", buf);
-
-#undef PUT_ENVP
-
- return 0;
-}
-
-#else
-
-static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- return -ENODEV;
-}
-
-#endif /* CONFIG_HOTPLUG */
-
-
-int __hpsb_register_protocol(struct hpsb_protocol_driver *drv,
- struct module *owner)
-{
- int error;
-
- drv->driver.bus = &ieee1394_bus_type;
- drv->driver.owner = owner;
- drv->driver.name = drv->name;
-
- /* This will cause a probe for devices */
- error = driver_register(&drv->driver);
- if (!error)
- nodemgr_create_drv_files(drv);
- return error;
-}
-
-void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
-{
- nodemgr_remove_drv_files(driver);
- /* This will subsequently disconnect all devices that our driver
- * is attached to. */
- driver_unregister(&driver->driver);
-}
-
-
-/*
- * This function updates nodes that were present on the bus before the
- * reset and still are after the reset. The nodeid and the config rom
- * may have changed, and the drivers managing this device must be
- * informed that this device just went through a bus reset, to allow
- * the to take whatever actions required.
- */
-static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr,
- nodeid_t nodeid, unsigned int generation)
-{
- if (ne->nodeid != nodeid) {
- HPSB_DEBUG("Node changed: " NODE_BUS_FMT " -> " NODE_BUS_FMT,
- NODE_BUS_ARGS(ne->host, ne->nodeid),
- NODE_BUS_ARGS(ne->host, nodeid));
- ne->nodeid = nodeid;
- }
-
- if (ne->busopt.generation != ((be32_to_cpu(csr->bus_info_data[2]) >> 4) & 0xf)) {
- kfree(ne->csr->private);
- csr1212_destroy_csr(ne->csr);
- ne->csr = csr;
-
- /* If the node's configrom generation has changed, we
- * unregister all the unit directories. */
- nodemgr_remove_uds(ne);
-
- nodemgr_update_bus_options(ne);
-
- /* Mark the node as new, so it gets re-probed */
- ne->needs_probe = true;
- } else {
- /* old cache is valid, so update its generation */
- struct nodemgr_csr_info *ci = ne->csr->private;
- ci->generation = generation;
- /* free the partially filled now unneeded new cache */
- kfree(csr->private);
- csr1212_destroy_csr(csr);
- }
-
- /* Finally, mark the node current */
- smp_wmb();
- ne->generation = generation;
-
- if (ne->in_limbo) {
- device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
- ne->in_limbo = false;
-
- HPSB_DEBUG("Node reactivated: "
- "ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
- NODE_BUS_ARGS(ne->host, ne->nodeid),
- (unsigned long long)ne->guid);
- }
-}
-
-static void nodemgr_node_scan_one(struct hpsb_host *host,
- nodeid_t nodeid, int generation)
-{
- struct node_entry *ne;
- octlet_t guid;
- struct csr1212_csr *csr;
- struct nodemgr_csr_info *ci;
- u8 *speed;
-
- ci = kmalloc(sizeof(*ci), GFP_KERNEL);
- kmemcheck_annotate_bitfield(ci, flags);
- if (!ci)
- return;
-
- ci->host = host;
- ci->nodeid = nodeid;
- ci->generation = generation;
-
- /* Prepare for speed probe which occurs when reading the ROM */
- speed = &(host->speed[NODEID_TO_NODE(nodeid)]);
- if (*speed > host->csr.lnk_spd)
- *speed = host->csr.lnk_spd;
- ci->speed_unverified = *speed > IEEE1394_SPEED_100;
-
- /* We need to detect when the ConfigROM's generation has changed,
- * so we only update the node's info when it needs to be. */
-
- csr = csr1212_create_csr(&nodemgr_csr_ops, 5 * sizeof(quadlet_t), ci);
- if (!csr || csr1212_parse_csr(csr) != CSR1212_SUCCESS) {
- HPSB_ERR("Error parsing configrom for node " NODE_BUS_FMT,
- NODE_BUS_ARGS(host, nodeid));
- if (csr)
- csr1212_destroy_csr(csr);
- kfree(ci);
- return;
- }
-
- if (csr->bus_info_data[1] != IEEE1394_BUSID_MAGIC) {
- /* This isn't a 1394 device, but we let it slide. There
- * was a report of a device with broken firmware which
- * reported '2394' instead of '1394', which is obviously a
- * mistake. One would hope that a non-1394 device never
- * gets connected to Firewire bus. If someone does, we
- * shouldn't be held responsible, so we'll allow it with a
- * warning. */
- HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]",
- NODE_BUS_ARGS(host, nodeid), csr->bus_info_data[1]);
- }
-
- guid = ((u64)be32_to_cpu(csr->bus_info_data[3]) << 32) | be32_to_cpu(csr->bus_info_data[4]);
- ne = find_entry_by_guid(guid);
-
- if (ne && ne->host != host && ne->in_limbo) {
- /* Must have moved this device from one host to another */
- nodemgr_remove_ne(ne);
- ne = NULL;
- }
-
- if (!ne)
- nodemgr_create_node(guid, csr, host, nodeid, generation);
- else
- nodemgr_update_node(ne, csr, nodeid, generation);
-}
-
-
-static void nodemgr_node_scan(struct hpsb_host *host, int generation)
-{
- int count;
- struct selfid *sid = (struct selfid *)host->topology_map;
- nodeid_t nodeid = LOCAL_BUS;
-
- /* Scan each node on the bus */
- for (count = host->selfid_count; count; count--, sid++) {
- if (sid->extended)
- continue;
-
- if (!sid->link_active) {
- nodeid++;
- continue;
- }
- nodemgr_node_scan_one(host, nodeid++, generation);
- }
-}
-
-static void nodemgr_pause_ne(struct node_entry *ne)
-{
- HPSB_DEBUG("Node paused: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
- NODE_BUS_ARGS(ne->host, ne->nodeid),
- (unsigned long long)ne->guid);
-
- ne->in_limbo = true;
- WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo));
-}
-
-static int update_pdrv(struct device *dev, void *data)
-{
- struct unit_directory *ud;
- struct device_driver *drv;
- struct hpsb_protocol_driver *pdrv;
- struct node_entry *ne = data;
- int error;
-
- ud = container_of(dev, struct unit_directory, unit_dev);
- if (ud->ne == ne) {
- drv = get_driver(ud->device.driver);
- if (drv) {
- error = 0;
- pdrv = container_of(drv, struct hpsb_protocol_driver,
- driver);
- if (pdrv->update) {
- device_lock(&ud->device);
- error = pdrv->update(ud);
- device_unlock(&ud->device);
- }
- if (error)
- device_release_driver(&ud->device);
- put_driver(drv);
- }
- }
-
- return 0;
-}
-
-static void nodemgr_update_pdrv(struct node_entry *ne)
-{
- class_for_each_device(&nodemgr_ud_class, NULL, ne, update_pdrv);
-}
-
-/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3. This
- * seems like an optional service but in the end it is practically mandatory
- * as a consequence of these clauses.
- *
- * Note that we cannot do a broadcast write to all nodes at once because some
- * pre-1394a devices would hang. */
-static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
-{
- const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL);
- quadlet_t bc_remote, bc_local;
- int error;
-
- if (!ne->host->is_irm || ne->generation != generation ||
- ne->nodeid == ne->host->node_id)
- return;
-
- bc_local = cpu_to_be32(ne->host->csr.broadcast_channel);
-
- /* Check if the register is implemented and 1394a compliant. */
- error = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote,
- sizeof(bc_remote));
- if (!error && bc_remote & cpu_to_be32(0x80000000) &&
- bc_remote != bc_local)
- hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local));
-}
-
-
-static void nodemgr_probe_ne(struct hpsb_host *host, struct node_entry *ne,
- int generation)
-{
- struct device *dev;
-
- if (ne->host != host || ne->in_limbo)
- return;
-
- dev = get_device(&ne->device);
- if (!dev)
- return;
-
- nodemgr_irm_write_bc(ne, generation);
-
- /* If "needs_probe", then this is either a new or changed node we
- * rescan totally. If the generation matches for an existing node
- * (one that existed prior to the bus reset) we send update calls
- * down to the drivers. Otherwise, this is a dead node and we
- * suspend it. */
- if (ne->needs_probe)
- nodemgr_process_root_directory(ne);
- else if (ne->generation == generation)
- nodemgr_update_pdrv(ne);
- else
- nodemgr_pause_ne(ne);
-
- put_device(dev);
-}
-
-struct node_probe_parameter {
- struct hpsb_host *host;
- int generation;
- bool probe_now;
-};
-
-static int node_probe(struct device *dev, void *data)
-{
- struct node_probe_parameter *p = data;
- struct node_entry *ne;
-
- if (p->generation != get_hpsb_generation(p->host))
- return -EAGAIN;
-
- ne = container_of(dev, struct node_entry, node_dev);
- if (ne->needs_probe == p->probe_now)
- nodemgr_probe_ne(p->host, ne, p->generation);
- return 0;
-}
-
-static int nodemgr_node_probe(struct hpsb_host *host, int generation)
-{
- struct node_probe_parameter p;
-
- p.host = host;
- p.generation = generation;
- /*
- * Do some processing of the nodes we've probed. This pulls them
- * into the sysfs layer if needed, and can result in processing of
- * unit-directories, or just updating the node and it's
- * unit-directories.
- *
- * Run updates before probes. Usually, updates are time-critical
- * while probes are time-consuming.
- *
- * Meanwhile, another bus reset may have happened. In this case we
- * skip everything here and let the next bus scan handle it.
- * Otherwise we may prematurely remove nodes which are still there.
- */
- p.probe_now = false;
- if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0)
- return 0;
-
- p.probe_now = true;
- if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0)
- return 0;
- /*
- * Now let's tell the bus to rescan our devices. This may seem
- * like overhead, but the driver-model core will only scan a
- * device for a driver when either the device is added, or when a
- * new driver is added. A bus reset is a good reason to rescan
- * devices that were there before. For example, an sbp2 device
- * may become available for login, if the host that held it was
- * just removed.
- */
- if (bus_rescan_devices(&ieee1394_bus_type) != 0)
- HPSB_DEBUG("bus_rescan_devices had an error");
-
- return 1;
-}
-
-static int remove_nodes_in_limbo(struct device *dev, void *data)
-{
- struct node_entry *ne;
-
- if (dev->bus != &ieee1394_bus_type)
- return 0;
-
- ne = container_of(dev, struct node_entry, device);
- if (ne->in_limbo)
- nodemgr_remove_ne(ne);
-
- return 0;
-}
-
-static void nodemgr_remove_nodes_in_limbo(struct hpsb_host *host)
-{
- device_for_each_child(&host->device, NULL, remove_nodes_in_limbo);
-}
-
-static int nodemgr_send_resume_packet(struct hpsb_host *host)
-{
- struct hpsb_packet *packet;
- int error = -ENOMEM;
-
- packet = hpsb_make_phypacket(host,
- EXTPHYPACKET_TYPE_RESUME |
- NODEID_TO_NODE(host->node_id) << PHYPACKET_PORT_SHIFT);
- if (packet) {
- packet->no_waiter = 1;
- packet->generation = get_hpsb_generation(host);
- error = hpsb_send_packet(packet);
- }
- if (error)
- HPSB_WARN("fw-host%d: Failed to broadcast resume packet",
- host->id);
- return error;
-}
-
-/* Perform a few high-level IRM responsibilities. */
-static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
-{
- quadlet_t bc;
-
- /* if irm_id == -1 then there is no IRM on this bus */
- if (!host->is_irm || host->irm_id == (nodeid_t)-1)
- return 1;
-
- /* We are a 1394a-2000 compliant IRM. Set the validity bit. */
- host->csr.broadcast_channel |= 0x40000000;
-
- /* If there is no bus manager then we should set the root node's
- * force_root bit to promote bus stability per the 1394
- * spec. (8.4.2.6) */
- if (host->busmgr_id == 0xffff && host->node_count > 1)
- {
- u16 root_node = host->node_count - 1;
-
- /* get cycle master capability flag from root node */
- if (host->is_cycmst ||
- (!hpsb_read(host, LOCAL_BUS | root_node, get_hpsb_generation(host),
- (CSR_REGISTER_BASE + CSR_CONFIG_ROM + 2 * sizeof(quadlet_t)),
- &bc, sizeof(quadlet_t)) &&
- be32_to_cpu(bc) & 1 << CSR_CMC_SHIFT))
- hpsb_send_phy_config(host, root_node, -1);
- else {
- HPSB_DEBUG("The root node is not cycle master capable; "
- "selecting a new root node and resetting...");
-
- if (cycles >= 5) {
- /* Oh screw it! Just leave the bus as it is */
- HPSB_DEBUG("Stopping reset loop for IRM sanity");
- return 1;
- }
-
- hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1);
- hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT);
-
- return 0;
- }
- }
-
- /* Some devices suspend their ports while being connected to an inactive
- * host adapter, i.e. if connected before the low-level driver is
- * loaded. They become visible either when physically unplugged and
- * replugged, or when receiving a resume packet. Send one once. */
- if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host))
- host->resume_packet_sent = 1;
-
- return 1;
-}
-
-/* We need to ensure that if we are not the IRM, that the IRM node is capable of
- * everything we can do, otherwise issue a bus reset and try to become the IRM
- * ourselves. */
-static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles)
-{
- quadlet_t bc;
- int status;
-
- if (hpsb_disable_irm || host->is_irm)
- return 1;
-
- status = hpsb_read(host, LOCAL_BUS | (host->irm_id),
- get_hpsb_generation(host),
- (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL),
- &bc, sizeof(quadlet_t));
-
- if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) {
- /* The current irm node does not have a valid BROADCAST_CHANNEL
- * register and we do, so reset the bus with force_root set */
- HPSB_DEBUG("Current remote IRM is not 1394a-2000 compliant, resetting...");
-
- if (cycles >= 5) {
- /* Oh screw it! Just leave the bus as it is */
- HPSB_DEBUG("Stopping reset loop for IRM sanity");
- return 1;
- }
-
- hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1);
- hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT);
-
- return 0;
- }
-
- return 1;
-}
-
-static int nodemgr_host_thread(void *data)
-{
- struct hpsb_host *host = data;
- unsigned int g, generation = 0;
- int i, reset_cycles = 0;
-
- set_freezable();
- /* Setup our device-model entries */
- nodemgr_create_host_dev_files(host);
-
- for (;;) {
- /* Sleep until next bus reset */
- set_current_state(TASK_INTERRUPTIBLE);
- if (get_hpsb_generation(host) == generation &&
- !kthread_should_stop())
- schedule();
- __set_current_state(TASK_RUNNING);
-
- /* Thread may have been woken up to freeze or to exit */
- if (try_to_freeze())
- continue;
- if (kthread_should_stop())
- goto exit;
-
- /* Pause for 1/4 second in 1/16 second intervals,
- * to make sure things settle down. */
- g = get_hpsb_generation(host);
- for (i = 0; i < 4 ; i++) {
- msleep_interruptible(63);
- try_to_freeze();
- if (kthread_should_stop())
- goto exit;
-
- /* Now get the generation in which the node ID's we collect
- * are valid. During the bus scan we will use this generation
- * for the read transactions, so that if another reset occurs
- * during the scan the transactions will fail instead of
- * returning bogus data. */
- generation = get_hpsb_generation(host);
-
- /* If we get a reset before we are done waiting, then
- * start the waiting over again */
- if (generation != g)
- g = generation, i = 0;
- }
-
- if (!nodemgr_check_irm_capability(host, reset_cycles) ||
- !nodemgr_do_irm_duties(host, reset_cycles)) {
- reset_cycles++;
- continue;
- }
- reset_cycles = 0;
-
- /* Scan our nodes to get the bus options and create node
- * entries. This does not do the sysfs stuff, since that
- * would trigger uevents and such, which is a bad idea at
- * this point. */
- nodemgr_node_scan(host, generation);
-
- /* This actually does the full probe, with sysfs
- * registration. */
- if (!nodemgr_node_probe(host, generation))
- continue;
-
- /* Update some of our sysfs symlinks */
- nodemgr_update_host_dev_links(host);
-
- /* Sleep 3 seconds */
- for (i = 3000/200; i; i--) {
- msleep_interruptible(200);
- try_to_freeze();
- if (kthread_should_stop())
- goto exit;
-
- if (generation != get_hpsb_generation(host))
- break;
- }
- /* Remove nodes which are gone, unless a bus reset happened */
- if (!i)
- nodemgr_remove_nodes_in_limbo(host);
- }
-exit:
- HPSB_VERBOSE("NodeMgr: Exiting thread");
- return 0;
-}
-
-struct per_host_parameter {
- void *data;
- int (*cb)(struct hpsb_host *, void *);
-};
-
-static int per_host(struct device *dev, void *data)
-{
- struct hpsb_host *host;
- struct per_host_parameter *p = data;
-
- host = container_of(dev, struct hpsb_host, host_dev);
- return p->cb(host, p->data);
-}
-
-/**
- * nodemgr_for_each_host - call a function for each IEEE 1394 host
- * @data: an address to supply to the callback
- * @cb: function to call for each host
- *
- * Iterate the hosts, calling a given function with supplied data for each host.
- * If the callback fails on a host, i.e. if it returns a non-zero value, the
- * iteration is stopped.
- *
- * Return value: 0 on success, non-zero on failure (same as returned by last run
- * of the callback).
- */
-int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *))
-{
- struct per_host_parameter p;
-
- p.cb = cb;
- p.data = data;
- return class_for_each_device(&hpsb_host_class, NULL, &p, per_host);
-}
-
-/* The following two convenience functions use a struct node_entry
- * for addressing a node on the bus. They are intended for use by any
- * process context, not just the nodemgr thread, so we need to be a
- * little careful when reading out the node ID and generation. The
- * thing that can go wrong is that we get the node ID, then a bus
- * reset occurs, and then we read the generation. The node ID is
- * possibly invalid, but the generation is current, and we end up
- * sending a packet to a the wrong node.
- *
- * The solution is to make sure we read the generation first, so that
- * if a reset occurs in the process, we end up with a stale generation
- * and the transactions will fail instead of silently using wrong node
- * ID's.
- */
-
-/**
- * hpsb_node_fill_packet - fill some destination information into a packet
- * @ne: destination node
- * @packet: packet to fill in
- *
- * This will fill in the given, pre-initialised hpsb_packet with the current
- * information from the node entry (host, node ID, bus generation number).
- */
-void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet)
-{
- packet->host = ne->host;
- packet->generation = ne->generation;
- smp_rmb();
- packet->node_id = ne->nodeid;
-}
-
-int hpsb_node_write(struct node_entry *ne, u64 addr,
- quadlet_t *buffer, size_t length)
-{
- unsigned int generation = ne->generation;
-
- smp_rmb();
- return hpsb_write(ne->host, ne->nodeid, generation,
- addr, buffer, length);
-}
-
-static void nodemgr_add_host(struct hpsb_host *host)
-{
- struct host_info *hi;
-
- hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi));
- if (!hi) {
- HPSB_ERR("NodeMgr: out of memory in add host");
- return;
- }
- hi->host = host;
- hi->thread = kthread_run(nodemgr_host_thread, host, "knodemgrd_%d",
- host->id);
- if (IS_ERR(hi->thread)) {
- HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id);
- hpsb_destroy_hostinfo(&nodemgr_highlevel, host);
- }
-}
-
-static void nodemgr_host_reset(struct hpsb_host *host)
-{
- struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
-
- if (hi) {
- HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id);
- wake_up_process(hi->thread);
- }
-}
-
-static void nodemgr_remove_host(struct hpsb_host *host)
-{
- struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
-
- if (hi) {
- kthread_stop(hi->thread);
- nodemgr_remove_host_dev(&host->device);
- }
-}
-
-static struct hpsb_highlevel nodemgr_highlevel = {
- .name = "Node manager",
- .add_host = nodemgr_add_host,
- .host_reset = nodemgr_host_reset,
- .remove_host = nodemgr_remove_host,
-};
-
-int init_ieee1394_nodemgr(void)
-{
- int error;
-
- error = class_register(&nodemgr_ne_class);
- if (error)
- goto fail_ne;
- error = class_register(&nodemgr_ud_class);
- if (error)
- goto fail_ud;
- error = driver_register(&nodemgr_mid_layer_driver);
- if (error)
- goto fail_ml;
- /* This driver is not used if nodemgr is off (disable_nodemgr=1). */
- nodemgr_dev_template_host.driver = &nodemgr_mid_layer_driver;
-
- hpsb_register_highlevel(&nodemgr_highlevel);
- return 0;
-
-fail_ml:
- class_unregister(&nodemgr_ud_class);
-fail_ud:
- class_unregister(&nodemgr_ne_class);
-fail_ne:
- return error;
-}
-
-void cleanup_ieee1394_nodemgr(void)
-{
- hpsb_unregister_highlevel(&nodemgr_highlevel);
- driver_unregister(&nodemgr_mid_layer_driver);
- class_unregister(&nodemgr_ud_class);
- class_unregister(&nodemgr_ne_class);
-}
diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h
deleted file mode 100644
index 749b271d3107..000000000000
--- a/drivers/ieee1394/nodemgr.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2000 Andreas E. Bombe
- * 2001 Ben Collins <bcollins@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef _IEEE1394_NODEMGR_H
-#define _IEEE1394_NODEMGR_H
-
-#include <linux/device.h>
-#include <asm/system.h>
-#include <asm/types.h>
-
-#include "ieee1394_core.h"
-#include "ieee1394_transactions.h"
-#include "ieee1394_types.h"
-
-struct csr1212_csr;
-struct csr1212_keyval;
-struct hpsb_host;
-struct ieee1394_device_id;
-
-/* This is the start of a Node entry structure. It should be a stable API
- * for which to gather info from the Node Manager about devices attached
- * to the bus. */
-struct bus_options {
- u8 irmc; /* Iso Resource Manager Capable */
- u8 cmc; /* Cycle Master Capable */
- u8 isc; /* Iso Capable */
- u8 bmc; /* Bus Master Capable */
- u8 pmc; /* Power Manager Capable (PNP spec) */
- u8 cyc_clk_acc; /* Cycle clock accuracy */
- u8 max_rom; /* Maximum block read supported in the CSR */
- u8 generation; /* Incremented when configrom changes */
- u8 lnkspd; /* Link speed */
- u16 max_rec; /* Maximum packet size node can receive */
-};
-
-#define UNIT_DIRECTORY_VENDOR_ID 0x01
-#define UNIT_DIRECTORY_MODEL_ID 0x02
-#define UNIT_DIRECTORY_SPECIFIER_ID 0x04
-#define UNIT_DIRECTORY_VERSION 0x08
-#define UNIT_DIRECTORY_HAS_LUN_DIRECTORY 0x10
-#define UNIT_DIRECTORY_LUN_DIRECTORY 0x20
-#define UNIT_DIRECTORY_HAS_LUN 0x40
-
-/*
- * A unit directory corresponds to a protocol supported by the
- * node. If a node supports eg. IP/1394 and AV/C, its config rom has a
- * unit directory for each of these protocols.
- */
-struct unit_directory {
- struct node_entry *ne; /* The node which this directory belongs to */
- octlet_t address; /* Address of the unit directory on the node */
- u8 flags; /* Indicates which entries were read */
-
- quadlet_t vendor_id;
- struct csr1212_keyval *vendor_name_kv;
-
- quadlet_t model_id;
- struct csr1212_keyval *model_name_kv;
- quadlet_t specifier_id;
- quadlet_t version;
- quadlet_t directory_id;
-
- unsigned int id;
-
- int ignore_driver;
-
- int length; /* Number of quadlets */
-
- struct device device;
- struct device unit_dev;
-
- struct csr1212_keyval *ud_kv;
- u32 lun; /* logical unit number immediate value */
-};
-
-struct node_entry {
- u64 guid; /* GUID of this node */
- u32 guid_vendor_id; /* Top 24bits of guid */
-
- struct hpsb_host *host; /* Host this node is attached to */
- nodeid_t nodeid; /* NodeID */
- struct bus_options busopt; /* Bus Options */
- bool needs_probe;
- unsigned int generation; /* Synced with hpsb generation */
-
- /* The following is read from the config rom */
- u32 vendor_id;
- struct csr1212_keyval *vendor_name_kv;
-
- u32 capabilities;
-
- struct device device;
- struct device node_dev;
-
- /* Means this node is not attached anymore */
- bool in_limbo;
-
- struct csr1212_csr *csr;
-};
-
-struct hpsb_protocol_driver {
- /* The name of the driver, e.g. SBP2 or IP1394 */
- const char *name;
-
- /*
- * The device id table describing the protocols and/or devices
- * supported by this driver. This is used by the nodemgr to
- * decide if a driver could support a given node, but the
- * probe function below can implement further protocol
- * dependent or vendor dependent checking.
- */
- const struct ieee1394_device_id *id_table;
-
- /*
- * The update function is called when the node has just
- * survived a bus reset, i.e. it is still present on the bus.
- * However, it may be necessary to reestablish the connection
- * or login into the node again, depending on the protocol. If the
- * probe fails (returns non-zero), we unbind the driver from this
- * device.
- */
- int (*update)(struct unit_directory *ud);
-
- /* Our LDM structure */
- struct device_driver driver;
-};
-
-int __hpsb_register_protocol(struct hpsb_protocol_driver *, struct module *);
-static inline int hpsb_register_protocol(struct hpsb_protocol_driver *driver)
-{
- return __hpsb_register_protocol(driver, THIS_MODULE);
-}
-
-void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver);
-
-static inline int hpsb_node_entry_valid(struct node_entry *ne)
-{
- return ne->generation == get_hpsb_generation(ne->host);
-}
-void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet);
-int hpsb_node_write(struct node_entry *ne, u64 addr,
- quadlet_t *buffer, size_t length);
-static inline int hpsb_node_read(struct node_entry *ne, u64 addr,
- quadlet_t *buffer, size_t length)
-{
- unsigned int g = ne->generation;
-
- smp_rmb();
- return hpsb_read(ne->host, ne->nodeid, g, addr, buffer, length);
-}
-static inline int hpsb_node_lock(struct node_entry *ne, u64 addr, int extcode,
- quadlet_t *buffer, quadlet_t arg)
-{
- unsigned int g = ne->generation;
-
- smp_rmb();
- return hpsb_lock(ne->host, ne->nodeid, g, addr, extcode, buffer, arg);
-}
-int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *));
-
-int init_ieee1394_nodemgr(void);
-void cleanup_ieee1394_nodemgr(void);
-
-/* The template for a host device */
-extern struct device nodemgr_dev_template_host;
-
-/* Bus attributes we export */
-extern struct bus_attribute *const fw_bus_attrs[];
-
-#endif /* _IEEE1394_NODEMGR_H */
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
deleted file mode 100644
index 50815022cff1..000000000000
--- a/drivers/ieee1394/ohci1394.c
+++ /dev/null
@@ -1,3590 +0,0 @@
-/*
- * ohci1394.c - driver for OHCI 1394 boards
- * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
- * Gord Peters <GordPeters@smarttech.com>
- * 2001 Ben Collins <bcollins@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- */
-
-/*
- * Things known to be working:
- * . Async Request Transmit
- * . Async Response Receive
- * . Async Request Receive
- * . Async Response Transmit
- * . Iso Receive
- * . DMA mmap for iso receive
- * . Config ROM generation
- *
- * Things implemented, but still in test phase:
- * . Iso Transmit
- * . Async Stream Packets Transmit (Receive done via Iso interface)
- *
- * Things not implemented:
- * . DMA error recovery
- *
- * Known bugs:
- * . devctl BUS_RESET arg confusion (reset type or root holdoff?)
- * added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk
- */
-
-/*
- * Acknowledgments:
- *
- * Adam J Richter <adam@yggdrasil.com>
- * . Use of pci_class to find device
- *
- * Emilie Chung <emilie.chung@axis.com>
- * . Tip on Async Request Filter
- *
- * Pascal Drolet <pascal.drolet@informission.ca>
- * . Various tips for optimization and functionnalities
- *
- * Robert Ficklin <rficklin@westengineering.com>
- * . Loop in irq_handler
- *
- * James Goodwin <jamesg@Filanet.com>
- * . Various tips on initialization, self-id reception, etc.
- *
- * Albrecht Dress <ad@mpifr-bonn.mpg.de>
- * . Apple PowerBook detection
- *
- * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de>
- * . Reset the board properly before leaving + misc cleanups
- *
- * Leon van Stuivenberg <leonvs@iae.nl>
- * . Bug fixes
- *
- * Ben Collins <bcollins@debian.org>
- * . Working big-endian support
- * . Updated to 2.4.x module scheme (PCI aswell)
- * . Config ROM generation
- *
- * Manfred Weihs <weihs@ict.tuwien.ac.at>
- * . Reworked code for initiating bus resets
- * (long, short, with or without hold-off)
- *
- * Nandu Santhi <contactnandu@users.sourceforge.net>
- * . Added support for nVidia nForce2 onboard Firewire chipset
- *
- */
-
-#include <linux/bitops.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <asm/byteorder.h>
-#include <asm/atomic.h>
-#include <asm/uaccess.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <asm/irq.h>
-#include <linux/types.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-
-#ifdef CONFIG_PPC_PMAC
-#include <asm/machdep.h>
-#include <asm/pmac_feature.h>
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#endif
-
-#include "csr1212.h"
-#include "ieee1394.h"
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "dma.h"
-#include "iso.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "ohci1394.h"
-
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-#define OHCI1394_DEBUG
-#endif
-
-#ifdef DBGMSG
-#undef DBGMSG
-#endif
-
-#ifdef OHCI1394_DEBUG
-#define DBGMSG(fmt, args...) \
-printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
-#else
-#define DBGMSG(fmt, args...) do {} while (0)
-#endif
-
-/* print general (card independent) information */
-#define PRINT_G(level, fmt, args...) \
-printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args)
-
-/* print card specific information */
-#define PRINT(level, fmt, args...) \
-printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
-
-/* Module Parameters */
-static int phys_dma = 1;
-module_param(phys_dma, int, 0444);
-MODULE_PARM_DESC(phys_dma, "Enable physical DMA (default = 1).");
-
-static void dma_trm_tasklet(unsigned long data);
-static void dma_trm_reset(struct dma_trm_ctx *d);
-
-static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
- enum context_type type, int ctx, int num_desc,
- int buf_size, int split_buf_size, int context_base);
-static void free_dma_rcv_ctx(struct dma_rcv_ctx *d);
-
-static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
- enum context_type type, int ctx, int num_desc,
- int context_base);
-
-static void ohci1394_pci_remove(struct pci_dev *pdev);
-
-#ifndef __LITTLE_ENDIAN
-static const size_t hdr_sizes[] = {
- 3, /* TCODE_WRITEQ */
- 4, /* TCODE_WRITEB */
- 3, /* TCODE_WRITE_RESPONSE */
- 0, /* reserved */
- 3, /* TCODE_READQ */
- 4, /* TCODE_READB */
- 3, /* TCODE_READQ_RESPONSE */
- 4, /* TCODE_READB_RESPONSE */
- 1, /* TCODE_CYCLE_START */
- 4, /* TCODE_LOCK_REQUEST */
- 2, /* TCODE_ISO_DATA */
- 4, /* TCODE_LOCK_RESPONSE */
- /* rest is reserved or link-internal */
-};
-
-static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode)
-{
- size_t size;
-
- if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes)))
- return;
-
- size = hdr_sizes[tcode];
- while (size--)
- data[size] = le32_to_cpu(data[size]);
-}
-#else
-#define header_le32_to_cpu(w,x) do {} while (0)
-#endif /* !LITTLE_ENDIAN */
-
-/***********************************
- * IEEE-1394 functionality section *
- ***********************************/
-
-static u8 get_phy_reg(struct ti_ohci *ohci, u8 addr)
-{
- int i;
- unsigned long flags;
- quadlet_t r;
-
- spin_lock_irqsave (&ohci->phy_reg_lock, flags);
-
- reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000);
-
- for (i = 0; i < OHCI_LOOP_COUNT; i++) {
- if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000)
- break;
-
- mdelay(1);
- }
-
- r = reg_read(ohci, OHCI1394_PhyControl);
-
- if (i >= OHCI_LOOP_COUNT)
- PRINT (KERN_ERR, "Get PHY Reg timeout [0x%08x/0x%08x/%d]",
- r, r & 0x80000000, i);
-
- spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
-
- return (r & 0x00ff0000) >> 16;
-}
-
-static void set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data)
-{
- int i;
- unsigned long flags;
- u32 r = 0;
-
- spin_lock_irqsave (&ohci->phy_reg_lock, flags);
-
- reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000);
-
- for (i = 0; i < OHCI_LOOP_COUNT; i++) {
- r = reg_read(ohci, OHCI1394_PhyControl);
- if (!(r & 0x00004000))
- break;
-
- mdelay(1);
- }
-
- if (i == OHCI_LOOP_COUNT)
- PRINT (KERN_ERR, "Set PHY Reg timeout [0x%08x/0x%08x/%d]",
- r, r & 0x00004000, i);
-
- spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
-
- return;
-}
-
-/* Or's our value into the current value */
-static void set_phy_reg_mask(struct ti_ohci *ohci, u8 addr, u8 data)
-{
- u8 old;
-
- old = get_phy_reg (ohci, addr);
- old |= data;
- set_phy_reg (ohci, addr, old);
-
- return;
-}
-
-static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
- int phyid, int isroot)
-{
- quadlet_t *q = ohci->selfid_buf_cpu;
- quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount);
- size_t size;
- quadlet_t q0, q1;
-
- /* Check status of self-id reception */
-
- if (ohci->selfid_swap)
- q0 = le32_to_cpu(q[0]);
- else
- q0 = q[0];
-
- if ((self_id_count & 0x80000000) ||
- ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) {
- PRINT(KERN_ERR,
- "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)",
- self_id_count, q0, ohci->self_id_errors);
-
- /* Tip by James Goodwin <jamesg@Filanet.com>:
- * We had an error, generate another bus reset in response. */
- if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) {
- set_phy_reg_mask (ohci, 1, 0x40);
- ohci->self_id_errors++;
- } else {
- PRINT(KERN_ERR,
- "Too many errors on SelfID error reception, giving up!");
- }
- return;
- }
-
- /* SelfID Ok, reset error counter. */
- ohci->self_id_errors = 0;
-
- size = ((self_id_count & 0x00001FFC) >> 2) - 1;
- q++;
-
- while (size > 0) {
- if (ohci->selfid_swap) {
- q0 = le32_to_cpu(q[0]);
- q1 = le32_to_cpu(q[1]);
- } else {
- q0 = q[0];
- q1 = q[1];
- }
-
- if (q0 == ~q1) {
- DBGMSG ("SelfID packet 0x%x received", q0);
- hpsb_selfid_received(host, cpu_to_be32(q0));
- if (((q0 & 0x3f000000) >> 24) == phyid)
- DBGMSG ("SelfID for this node is 0x%08x", q0);
- } else {
- PRINT(KERN_ERR,
- "SelfID is inconsistent [0x%08x/0x%08x]", q0, q1);
- }
- q += 2;
- size -= 2;
- }
-
- DBGMSG("SelfID complete");
-
- return;
-}
-
-static void ohci_soft_reset(struct ti_ohci *ohci) {
- int i;
-
- reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
-
- for (i = 0; i < OHCI_LOOP_COUNT; i++) {
- if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset))
- break;
- mdelay(1);
- }
- DBGMSG ("Soft reset finished");
-}
-
-
-/* Generate the dma receive prgs and start the context */
-static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq)
-{
- struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
- int i;
-
- ohci1394_stop_context(ohci, d->ctrlClear, NULL);
-
- for (i=0; i<d->num_desc; i++) {
- u32 c;
-
- c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH;
- if (generate_irq)
- c |= DMA_CTL_IRQ;
-
- d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size);
-
- /* End of descriptor list? */
- if (i + 1 < d->num_desc) {
- d->prg_cpu[i]->branchAddress =
- cpu_to_le32((d->prg_bus[i+1] & 0xfffffff0) | 0x1);
- } else {
- d->prg_cpu[i]->branchAddress =
- cpu_to_le32((d->prg_bus[0] & 0xfffffff0));
- }
-
- d->prg_cpu[i]->address = cpu_to_le32(d->buf_bus[i]);
- d->prg_cpu[i]->status = cpu_to_le32(d->buf_size);
- }
-
- d->buf_ind = 0;
- d->buf_offset = 0;
-
- if (d->type == DMA_CTX_ISO) {
- /* Clear contextControl */
- reg_write(ohci, d->ctrlClear, 0xffffffff);
-
- /* Set bufferFill, isochHeader, multichannel for IR context */
- reg_write(ohci, d->ctrlSet, 0xd0000000);
-
- /* Set the context match register to match on all tags */
- reg_write(ohci, d->ctxtMatch, 0xf0000000);
-
- /* Clear the multi channel mask high and low registers */
- reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
-
- /* Set up isoRecvIntMask to generate interrupts */
- reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx);
- }
-
- /* Tell the controller where the first AR program is */
- reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1);
-
- /* Run context */
- reg_write(ohci, d->ctrlSet, 0x00008000);
-
- DBGMSG("Receive DMA ctx=%d initialized", d->ctx);
-}
-
-/* Initialize the dma transmit context */
-static void initialize_dma_trm_ctx(struct dma_trm_ctx *d)
-{
- struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
-
- /* Stop the context */
- ohci1394_stop_context(ohci, d->ctrlClear, NULL);
-
- d->prg_ind = 0;
- d->sent_ind = 0;
- d->free_prgs = d->num_desc;
- d->branchAddrPtr = NULL;
- INIT_LIST_HEAD(&d->fifo_list);
- INIT_LIST_HEAD(&d->pending_list);
-
- if (d->type == DMA_CTX_ISO) {
- /* enable interrupts */
- reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx);
- }
-
- DBGMSG("Transmit DMA ctx=%d initialized", d->ctx);
-}
-
-/* Count the number of available iso contexts */
-static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
-{
- u32 tmp;
-
- reg_write(ohci, reg, 0xffffffff);
- tmp = reg_read(ohci, reg);
-
- DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp);
-
- /* Count the number of contexts */
- return hweight32(tmp);
-}
-
-/* Global initialization */
-static void ohci_initialize(struct ti_ohci *ohci)
-{
- quadlet_t buf;
- int num_ports, i;
-
- spin_lock_init(&ohci->phy_reg_lock);
-
- /* Put some defaults to these undefined bus options */
- buf = reg_read(ohci, OHCI1394_BusOptions);
- buf |= 0x60000000; /* Enable CMC and ISC */
- if (hpsb_disable_irm)
- buf &= ~0x80000000;
- else
- buf |= 0x80000000; /* Enable IRMC */
- buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */
- buf &= ~0x18000000; /* Disable PMC and BMC */
- reg_write(ohci, OHCI1394_BusOptions, buf);
-
- /* Set the bus number */
- reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
-
- /* Enable posted writes */
- reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_postedWriteEnable);
-
- /* Clear link control register */
- reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
-
- /* Enable cycle timer and cycle master and set the IRM
- * contender bit in our self ID packets if appropriate. */
- reg_write(ohci, OHCI1394_LinkControlSet,
- OHCI1394_LinkControl_CycleTimerEnable |
- OHCI1394_LinkControl_CycleMaster);
- i = get_phy_reg(ohci, 4) | PHY_04_LCTRL;
- if (hpsb_disable_irm)
- i &= ~PHY_04_CONTENDER;
- else
- i |= PHY_04_CONTENDER;
- set_phy_reg(ohci, 4, i);
-
- /* Set up self-id dma buffer */
- reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus);
-
- /* enable self-id */
- reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID);
-
- /* Set the Config ROM mapping register */
- reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus);
-
- /* Now get our max packet size */
- ohci->max_packet_size =
- 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1);
-
- /* Clear the interrupt mask */
- reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
-
- /* Clear the interrupt mask */
- reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
-
- /* Initialize AR dma */
- initialize_dma_rcv_ctx(&ohci->ar_req_context, 0);
- initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0);
-
- /* Initialize AT dma */
- initialize_dma_trm_ctx(&ohci->at_req_context);
- initialize_dma_trm_ctx(&ohci->at_resp_context);
-
- /* Accept AR requests from all nodes */
- reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
-
- /* Set the address range of the physical response unit.
- * Most controllers do not implement it as a writable register though.
- * They will keep a hardwired offset of 0x00010000 and show 0x0 as
- * register content.
- * To actually enable physical responses is the job of our interrupt
- * handler which programs the physical request filter. */
- reg_write(ohci, OHCI1394_PhyUpperBound,
- OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED >> 16);
-
- DBGMSG("physUpperBoundOffset=%08x",
- reg_read(ohci, OHCI1394_PhyUpperBound));
-
- /* Specify AT retries */
- reg_write(ohci, OHCI1394_ATRetries,
- OHCI1394_MAX_AT_REQ_RETRIES |
- (OHCI1394_MAX_AT_RESP_RETRIES<<4) |
- (OHCI1394_MAX_PHYS_RESP_RETRIES<<8));
-
- /* We don't want hardware swapping */
- reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap);
-
- /* Enable interrupts */
- reg_write(ohci, OHCI1394_IntMaskSet,
- OHCI1394_unrecoverableError |
- OHCI1394_masterIntEnable |
- OHCI1394_busReset |
- OHCI1394_selfIDComplete |
- OHCI1394_RSPkt |
- OHCI1394_RQPkt |
- OHCI1394_respTxComplete |
- OHCI1394_reqTxComplete |
- OHCI1394_isochRx |
- OHCI1394_isochTx |
- OHCI1394_postedWriteErr |
- OHCI1394_cycleTooLong |
- OHCI1394_cycleInconsistent);
-
- /* Enable link */
- reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
-
- buf = reg_read(ohci, OHCI1394_Version);
- PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%d] "
- "MMIO=[%llx-%llx] Max Packet=[%d] IR/IT contexts=[%d/%d]",
- ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
- ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
- (unsigned long long)pci_resource_start(ohci->dev, 0),
- (unsigned long long)pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
- ohci->max_packet_size,
- ohci->nb_iso_rcv_ctx, ohci->nb_iso_xmit_ctx);
-
- /* Check all of our ports to make sure that if anything is
- * connected, we enable that port. */
- num_ports = get_phy_reg(ohci, 2) & 0xf;
- for (i = 0; i < num_ports; i++) {
- unsigned int status;
-
- set_phy_reg(ohci, 7, i);
- status = get_phy_reg(ohci, 8);
-
- if (status & 0x20)
- set_phy_reg(ohci, 8, status & ~1);
- }
-
- /* Serial EEPROM Sanity check. */
- if ((ohci->max_packet_size < 512) ||
- (ohci->max_packet_size > 4096)) {
- /* Serial EEPROM contents are suspect, set a sane max packet
- * size and print the raw contents for bug reports if verbose
- * debug is enabled. */
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
- int i;
-#endif
-
- PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, "
- "attempting to set max_packet_size to 512 bytes");
- reg_write(ohci, OHCI1394_BusOptions,
- (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002);
- ohci->max_packet_size = 512;
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
- PRINT(KERN_DEBUG, " EEPROM Present: %d",
- (reg_read(ohci, OHCI1394_Version) >> 24) & 0x1);
- reg_write(ohci, OHCI1394_GUID_ROM, 0x80000000);
-
- for (i = 0;
- ((i < 1000) &&
- (reg_read(ohci, OHCI1394_GUID_ROM) & 0x80000000)); i++)
- udelay(10);
-
- for (i = 0; i < 0x20; i++) {
- reg_write(ohci, OHCI1394_GUID_ROM, 0x02000000);
- PRINT(KERN_DEBUG, " EEPROM %02x: %02x", i,
- (reg_read(ohci, OHCI1394_GUID_ROM) >> 16) & 0xff);
- }
-#endif
- }
-}
-
-/*
- * Insert a packet in the DMA fifo and generate the DMA prg
- * FIXME: rewrite the program in order to accept packets crossing
- * page boundaries.
- * check also that a single dma descriptor doesn't cross a
- * page boundary.
- */
-static void insert_packet(struct ti_ohci *ohci,
- struct dma_trm_ctx *d, struct hpsb_packet *packet)
-{
- u32 cycleTimer;
- int idx = d->prg_ind;
-
- DBGMSG("Inserting packet for node " NODE_BUS_FMT
- ", tlabel=%d, tcode=0x%x, speed=%d",
- NODE_BUS_ARGS(ohci->host, packet->node_id), packet->tlabel,
- packet->tcode, packet->speed_code);
-
- d->prg_cpu[idx]->begin.address = 0;
- d->prg_cpu[idx]->begin.branchAddress = 0;
-
- if (d->type == DMA_CTX_ASYNC_RESP) {
- /*
- * For response packets, we need to put a timeout value in
- * the 16 lower bits of the status... let's try 1 sec timeout
- */
- cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
- d->prg_cpu[idx]->begin.status = cpu_to_le32(
- (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) |
- ((cycleTimer&0x01fff000)>>12));
-
- DBGMSG("cycleTimer: %08x timeStamp: %08x",
- cycleTimer, d->prg_cpu[idx]->begin.status);
- } else
- d->prg_cpu[idx]->begin.status = 0;
-
- if ( (packet->type == hpsb_async) || (packet->type == hpsb_raw) ) {
-
- if (packet->type == hpsb_raw) {
- d->prg_cpu[idx]->data[0] = cpu_to_le32(OHCI1394_TCODE_PHY<<4);
- d->prg_cpu[idx]->data[1] = cpu_to_le32(packet->header[0]);
- d->prg_cpu[idx]->data[2] = cpu_to_le32(packet->header[1]);
- } else {
- d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
- (packet->header[0] & 0xFFFF);
-
- if (packet->tcode == TCODE_ISO_DATA) {
- /* Sending an async stream packet */
- d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
- } else {
- /* Sending a normal async request or response */
- d->prg_cpu[idx]->data[1] =
- (packet->header[1] & 0xFFFF) |
- (packet->header[0] & 0xFFFF0000);
- d->prg_cpu[idx]->data[2] = packet->header[2];
- d->prg_cpu[idx]->data[3] = packet->header[3];
- }
- header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
- }
-
- if (packet->data_size) { /* block transmit */
- if (packet->tcode == TCODE_STREAM_DATA){
- d->prg_cpu[idx]->begin.control =
- cpu_to_le32(DMA_CTL_OUTPUT_MORE |
- DMA_CTL_IMMEDIATE | 0x8);
- } else {
- d->prg_cpu[idx]->begin.control =
- cpu_to_le32(DMA_CTL_OUTPUT_MORE |
- DMA_CTL_IMMEDIATE | 0x10);
- }
- d->prg_cpu[idx]->end.control =
- cpu_to_le32(DMA_CTL_OUTPUT_LAST |
- DMA_CTL_IRQ |
- DMA_CTL_BRANCH |
- packet->data_size);
- /*
- * Check that the packet data buffer
- * does not cross a page boundary.
- *
- * XXX Fix this some day. eth1394 seems to trigger
- * it, but ignoring it doesn't seem to cause a
- * problem.
- */
-#if 0
- if (cross_bound((unsigned long)packet->data,
- packet->data_size)>0) {
- /* FIXME: do something about it */
- PRINT(KERN_ERR,
- "%s: packet data addr: %p size %Zd bytes "
- "cross page boundary", __func__,
- packet->data, packet->data_size);
- }
-#endif
- d->prg_cpu[idx]->end.address = cpu_to_le32(
- pci_map_single(ohci->dev, packet->data,
- packet->data_size,
- PCI_DMA_TODEVICE));
-
- d->prg_cpu[idx]->end.branchAddress = 0;
- d->prg_cpu[idx]->end.status = 0;
- if (d->branchAddrPtr)
- *(d->branchAddrPtr) =
- cpu_to_le32(d->prg_bus[idx] | 0x3);
- d->branchAddrPtr =
- &(d->prg_cpu[idx]->end.branchAddress);
- } else { /* quadlet transmit */
- if (packet->type == hpsb_raw)
- d->prg_cpu[idx]->begin.control =
- cpu_to_le32(DMA_CTL_OUTPUT_LAST |
- DMA_CTL_IMMEDIATE |
- DMA_CTL_IRQ |
- DMA_CTL_BRANCH |
- (packet->header_size + 4));
- else
- d->prg_cpu[idx]->begin.control =
- cpu_to_le32(DMA_CTL_OUTPUT_LAST |
- DMA_CTL_IMMEDIATE |
- DMA_CTL_IRQ |
- DMA_CTL_BRANCH |
- packet->header_size);
-
- if (d->branchAddrPtr)
- *(d->branchAddrPtr) =
- cpu_to_le32(d->prg_bus[idx] | 0x2);
- d->branchAddrPtr =
- &(d->prg_cpu[idx]->begin.branchAddress);
- }
-
- } else { /* iso packet */
- d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
- (packet->header[0] & 0xFFFF);
- d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
- header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
-
- d->prg_cpu[idx]->begin.control =
- cpu_to_le32(DMA_CTL_OUTPUT_MORE |
- DMA_CTL_IMMEDIATE | 0x8);
- d->prg_cpu[idx]->end.control =
- cpu_to_le32(DMA_CTL_OUTPUT_LAST |
- DMA_CTL_UPDATE |
- DMA_CTL_IRQ |
- DMA_CTL_BRANCH |
- packet->data_size);
- d->prg_cpu[idx]->end.address = cpu_to_le32(
- pci_map_single(ohci->dev, packet->data,
- packet->data_size, PCI_DMA_TODEVICE));
-
- d->prg_cpu[idx]->end.branchAddress = 0;
- d->prg_cpu[idx]->end.status = 0;
- DBGMSG("Iso xmit context info: header[%08x %08x]\n"
- " begin=%08x %08x %08x %08x\n"
- " %08x %08x %08x %08x\n"
- " end =%08x %08x %08x %08x",
- d->prg_cpu[idx]->data[0], d->prg_cpu[idx]->data[1],
- d->prg_cpu[idx]->begin.control,
- d->prg_cpu[idx]->begin.address,
- d->prg_cpu[idx]->begin.branchAddress,
- d->prg_cpu[idx]->begin.status,
- d->prg_cpu[idx]->data[0],
- d->prg_cpu[idx]->data[1],
- d->prg_cpu[idx]->data[2],
- d->prg_cpu[idx]->data[3],
- d->prg_cpu[idx]->end.control,
- d->prg_cpu[idx]->end.address,
- d->prg_cpu[idx]->end.branchAddress,
- d->prg_cpu[idx]->end.status);
- if (d->branchAddrPtr)
- *(d->branchAddrPtr) = cpu_to_le32(d->prg_bus[idx] | 0x3);
- d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress);
- }
- d->free_prgs--;
-
- /* queue the packet in the appropriate context queue */
- list_add_tail(&packet->driver_list, &d->fifo_list);
- d->prg_ind = (d->prg_ind + 1) % d->num_desc;
-}
-
-/*
- * This function fills the FIFO with the (eventual) pending packets
- * and runs or wakes up the DMA prg if necessary.
- *
- * The function MUST be called with the d->lock held.
- */
-static void dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d)
-{
- struct hpsb_packet *packet, *ptmp;
- int idx = d->prg_ind;
- int z = 0;
-
- /* insert the packets into the dma fifo */
- list_for_each_entry_safe(packet, ptmp, &d->pending_list, driver_list) {
- if (!d->free_prgs)
- break;
-
- /* For the first packet only */
- if (!z)
- z = (packet->data_size) ? 3 : 2;
-
- /* Insert the packet */
- list_del_init(&packet->driver_list);
- insert_packet(ohci, d, packet);
- }
-
- /* Nothing must have been done, either no free_prgs or no packets */
- if (z == 0)
- return;
-
- /* Is the context running ? (should be unless it is
- the first packet to be sent in this context) */
- if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) {
- u32 nodeId = reg_read(ohci, OHCI1394_NodeID);
-
- DBGMSG("Starting transmit DMA ctx=%d",d->ctx);
- reg_write(ohci, d->cmdPtr, d->prg_bus[idx] | z);
-
- /* Check that the node id is valid, and not 63 */
- if (!(nodeId & 0x80000000) || (nodeId & 0x3f) == 63)
- PRINT(KERN_ERR, "Running dma failed because Node ID is not valid");
- else
- reg_write(ohci, d->ctrlSet, 0x8000);
- } else {
- /* Wake up the dma context if necessary */
- if (!(reg_read(ohci, d->ctrlSet) & 0x400))
- DBGMSG("Waking transmit DMA ctx=%d",d->ctx);
-
- /* do this always, to avoid race condition */
- reg_write(ohci, d->ctrlSet, 0x1000);
- }
-
- return;
-}
-
-/* Transmission of an async or iso packet */
-static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
-{
- struct ti_ohci *ohci = host->hostdata;
- struct dma_trm_ctx *d;
- unsigned long flags;
-
- if (packet->data_size > ohci->max_packet_size) {
- PRINT(KERN_ERR,
- "Transmit packet size %Zd is too big",
- packet->data_size);
- return -EOVERFLOW;
- }
-
- if (packet->type == hpsb_raw)
- d = &ohci->at_req_context;
- else if ((packet->tcode & 0x02) && (packet->tcode != TCODE_ISO_DATA))
- d = &ohci->at_resp_context;
- else
- d = &ohci->at_req_context;
-
- spin_lock_irqsave(&d->lock,flags);
-
- list_add_tail(&packet->driver_list, &d->pending_list);
-
- dma_trm_flush(ohci, d);
-
- spin_unlock_irqrestore(&d->lock,flags);
-
- return 0;
-}
-
-static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
-{
- struct ti_ohci *ohci = host->hostdata;
- int retval = 0, phy_reg;
-
- switch (cmd) {
- case RESET_BUS:
- switch (arg) {
- case SHORT_RESET:
- phy_reg = get_phy_reg(ohci, 5);
- phy_reg |= 0x40;
- set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
- break;
- case LONG_RESET:
- phy_reg = get_phy_reg(ohci, 1);
- phy_reg |= 0x40;
- set_phy_reg(ohci, 1, phy_reg); /* set IBR */
- break;
- case SHORT_RESET_NO_FORCE_ROOT:
- phy_reg = get_phy_reg(ohci, 1);
- if (phy_reg & 0x80) {
- phy_reg &= ~0x80;
- set_phy_reg(ohci, 1, phy_reg); /* clear RHB */
- }
-
- phy_reg = get_phy_reg(ohci, 5);
- phy_reg |= 0x40;
- set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
- break;
- case LONG_RESET_NO_FORCE_ROOT:
- phy_reg = get_phy_reg(ohci, 1);
- phy_reg &= ~0x80;
- phy_reg |= 0x40;
- set_phy_reg(ohci, 1, phy_reg); /* clear RHB, set IBR */
- break;
- case SHORT_RESET_FORCE_ROOT:
- phy_reg = get_phy_reg(ohci, 1);
- if (!(phy_reg & 0x80)) {
- phy_reg |= 0x80;
- set_phy_reg(ohci, 1, phy_reg); /* set RHB */
- }
-
- phy_reg = get_phy_reg(ohci, 5);
- phy_reg |= 0x40;
- set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
- break;
- case LONG_RESET_FORCE_ROOT:
- phy_reg = get_phy_reg(ohci, 1);
- phy_reg |= 0xc0;
- set_phy_reg(ohci, 1, phy_reg); /* set RHB and IBR */
- break;
- default:
- retval = -1;
- }
- break;
-
- case GET_CYCLE_COUNTER:
- retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
- break;
-
- case SET_CYCLE_COUNTER:
- reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg);
- break;
-
- case SET_BUS_ID:
- PRINT(KERN_ERR, "devctl command SET_BUS_ID err");
- break;
-
- case ACT_CYCLE_MASTER:
- if (arg) {
- /* check if we are root and other nodes are present */
- u32 nodeId = reg_read(ohci, OHCI1394_NodeID);
- if ((nodeId & (1<<30)) && (nodeId & 0x3f)) {
- /*
- * enable cycleTimer, cycleMaster
- */
- DBGMSG("Cycle master enabled");
- reg_write(ohci, OHCI1394_LinkControlSet,
- OHCI1394_LinkControl_CycleTimerEnable |
- OHCI1394_LinkControl_CycleMaster);
- }
- } else {
- /* disable cycleTimer, cycleMaster, cycleSource */
- reg_write(ohci, OHCI1394_LinkControlClear,
- OHCI1394_LinkControl_CycleTimerEnable |
- OHCI1394_LinkControl_CycleMaster |
- OHCI1394_LinkControl_CycleSource);
- }
- break;
-
- case CANCEL_REQUESTS:
- DBGMSG("Cancel request received");
- dma_trm_reset(&ohci->at_req_context);
- dma_trm_reset(&ohci->at_resp_context);
- break;
-
- default:
- PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet",
- cmd);
- break;
- }
- return retval;
-}
-
-/***********************************
- * rawiso ISO reception *
- ***********************************/
-
-/*
- We use either buffer-fill or packet-per-buffer DMA mode. The DMA
- buffer is split into "blocks" (regions described by one DMA
- descriptor). Each block must be one page or less in size, and
- must not cross a page boundary.
-
- There is one little wrinkle with buffer-fill mode: a packet that
- starts in the final block may wrap around into the first block. But
- the user API expects all packets to be contiguous. Our solution is
- to keep the very last page of the DMA buffer in reserve - if a
- packet spans the gap, we copy its tail into this page.
-*/
-
-struct ohci_iso_recv {
- struct ti_ohci *ohci;
-
- struct ohci1394_iso_tasklet task;
- int task_active;
-
- enum { BUFFER_FILL_MODE = 0,
- PACKET_PER_BUFFER_MODE = 1 } dma_mode;
-
- /* memory and PCI mapping for the DMA descriptors */
- struct dma_prog_region prog;
- struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */
-
- /* how many DMA blocks fit in the buffer */
- unsigned int nblocks;
-
- /* stride of DMA blocks */
- unsigned int buf_stride;
-
- /* number of blocks to batch between interrupts */
- int block_irq_interval;
-
- /* block that DMA will finish next */
- int block_dma;
-
- /* (buffer-fill only) block that the reader will release next */
- int block_reader;
-
- /* (buffer-fill only) bytes of buffer the reader has released,
- less than one block */
- int released_bytes;
-
- /* (buffer-fill only) buffer offset at which the next packet will appear */
- int dma_offset;
-
- /* OHCI DMA context control registers */
- u32 ContextControlSet;
- u32 ContextControlClear;
- u32 CommandPtr;
- u32 ContextMatch;
-};
-
-static void ohci_iso_recv_task(unsigned long data);
-static void ohci_iso_recv_stop(struct hpsb_iso *iso);
-static void ohci_iso_recv_shutdown(struct hpsb_iso *iso);
-static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync);
-static void ohci_iso_recv_program(struct hpsb_iso *iso);
-
-static int ohci_iso_recv_init(struct hpsb_iso *iso)
-{
- struct ti_ohci *ohci = iso->host->hostdata;
- struct ohci_iso_recv *recv;
- int ctx;
- int ret = -ENOMEM;
-
- recv = kmalloc(sizeof(*recv), GFP_KERNEL);
- if (!recv)
- return -ENOMEM;
-
- iso->hostdata = recv;
- recv->ohci = ohci;
- recv->task_active = 0;
- dma_prog_region_init(&recv->prog);
- recv->block = NULL;
-
- /* use buffer-fill mode, unless irq_interval is 1
- (note: multichannel requires buffer-fill) */
-
- if (((iso->irq_interval == 1 && iso->dma_mode == HPSB_ISO_DMA_OLD_ABI) ||
- iso->dma_mode == HPSB_ISO_DMA_PACKET_PER_BUFFER) && iso->channel != -1) {
- recv->dma_mode = PACKET_PER_BUFFER_MODE;
- } else {
- recv->dma_mode = BUFFER_FILL_MODE;
- }
-
- /* set nblocks, buf_stride, block_irq_interval */
-
- if (recv->dma_mode == BUFFER_FILL_MODE) {
- recv->buf_stride = PAGE_SIZE;
-
- /* one block per page of data in the DMA buffer, minus the final guard page */
- recv->nblocks = iso->buf_size/PAGE_SIZE - 1;
- if (recv->nblocks < 3) {
- DBGMSG("ohci_iso_recv_init: DMA buffer too small");
- goto err;
- }
-
- /* iso->irq_interval is in packets - translate that to blocks */
- if (iso->irq_interval == 1)
- recv->block_irq_interval = 1;
- else
- recv->block_irq_interval = iso->irq_interval *
- ((recv->nblocks+1)/iso->buf_packets);
- if (recv->block_irq_interval*4 > recv->nblocks)
- recv->block_irq_interval = recv->nblocks/4;
- if (recv->block_irq_interval < 1)
- recv->block_irq_interval = 1;
-
- } else {
- int max_packet_size;
-
- recv->nblocks = iso->buf_packets;
- recv->block_irq_interval = iso->irq_interval;
- if (recv->block_irq_interval * 4 > iso->buf_packets)
- recv->block_irq_interval = iso->buf_packets / 4;
- if (recv->block_irq_interval < 1)
- recv->block_irq_interval = 1;
-
- /* choose a buffer stride */
- /* must be a power of 2, and <= PAGE_SIZE */
-
- max_packet_size = iso->buf_size / iso->buf_packets;
-
- for (recv->buf_stride = 8; recv->buf_stride < max_packet_size;
- recv->buf_stride *= 2);
-
- if (recv->buf_stride*iso->buf_packets > iso->buf_size ||
- recv->buf_stride > PAGE_SIZE) {
- /* this shouldn't happen, but anyway... */
- DBGMSG("ohci_iso_recv_init: problem choosing a buffer stride");
- goto err;
- }
- }
-
- recv->block_reader = 0;
- recv->released_bytes = 0;
- recv->block_dma = 0;
- recv->dma_offset = 0;
-
- /* size of DMA program = one descriptor per block */
- if (dma_prog_region_alloc(&recv->prog,
- sizeof(struct dma_cmd) * recv->nblocks,
- recv->ohci->dev))
- goto err;
-
- recv->block = (struct dma_cmd*) recv->prog.kvirt;
-
- ohci1394_init_iso_tasklet(&recv->task,
- iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE :
- OHCI_ISO_RECEIVE,
- ohci_iso_recv_task, (unsigned long) iso);
-
- if (ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0) {
- ret = -EBUSY;
- goto err;
- }
-
- recv->task_active = 1;
-
- /* recv context registers are spaced 32 bytes apart */
- ctx = recv->task.context;
- recv->ContextControlSet = OHCI1394_IsoRcvContextControlSet + 32 * ctx;
- recv->ContextControlClear = OHCI1394_IsoRcvContextControlClear + 32 * ctx;
- recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx;
- recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx;
-
- if (iso->channel == -1) {
- /* clear multi-channel selection mask */
- reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF);
- reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF);
- }
-
- /* write the DMA program */
- ohci_iso_recv_program(iso);
-
- DBGMSG("ohci_iso_recv_init: %s mode, DMA buffer is %lu pages"
- " (%u bytes), using %u blocks, buf_stride %u, block_irq_interval %d",
- recv->dma_mode == BUFFER_FILL_MODE ?
- "buffer-fill" : "packet-per-buffer",
- iso->buf_size/PAGE_SIZE, iso->buf_size,
- recv->nblocks, recv->buf_stride, recv->block_irq_interval);
-
- return 0;
-
-err:
- ohci_iso_recv_shutdown(iso);
- return ret;
-}
-
-static void ohci_iso_recv_stop(struct hpsb_iso *iso)
-{
- struct ohci_iso_recv *recv = iso->hostdata;
-
- /* disable interrupts */
- reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context);
-
- /* halt DMA */
- ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL);
-}
-
-static void ohci_iso_recv_shutdown(struct hpsb_iso *iso)
-{
- struct ohci_iso_recv *recv = iso->hostdata;
-
- if (recv->task_active) {
- ohci_iso_recv_stop(iso);
- ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task);
- recv->task_active = 0;
- }
-
- dma_prog_region_free(&recv->prog);
- kfree(recv);
- iso->hostdata = NULL;
-}
-
-/* set up a "gapped" ring buffer DMA program */
-static void ohci_iso_recv_program(struct hpsb_iso *iso)
-{
- struct ohci_iso_recv *recv = iso->hostdata;
- int blk;
-
- /* address of 'branch' field in previous DMA descriptor */
- u32 *prev_branch = NULL;
-
- for (blk = 0; blk < recv->nblocks; blk++) {
- u32 control;
-
- /* the DMA descriptor */
- struct dma_cmd *cmd = &recv->block[blk];
-
- /* offset of the DMA descriptor relative to the DMA prog buffer */
- unsigned long prog_offset = blk * sizeof(struct dma_cmd);
-
- /* offset of this packet's data within the DMA buffer */
- unsigned long buf_offset = blk * recv->buf_stride;
-
- if (recv->dma_mode == BUFFER_FILL_MODE) {
- control = 2 << 28; /* INPUT_MORE */
- } else {
- control = 3 << 28; /* INPUT_LAST */
- }
-
- control |= 8 << 24; /* s = 1, update xferStatus and resCount */
-
- /* interrupt on last block, and at intervals */
- if (blk == recv->nblocks-1 || (blk % recv->block_irq_interval) == 0) {
- control |= 3 << 20; /* want interrupt */
- }
-
- control |= 3 << 18; /* enable branch to address */
- control |= recv->buf_stride;
-
- cmd->control = cpu_to_le32(control);
- cmd->address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, buf_offset));
- cmd->branchAddress = 0; /* filled in on next loop */
- cmd->status = cpu_to_le32(recv->buf_stride);
-
- /* link the previous descriptor to this one */
- if (prev_branch) {
- *prev_branch = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, prog_offset) | 1);
- }
-
- prev_branch = &cmd->branchAddress;
- }
-
- /* the final descriptor's branch address and Z should be left at 0 */
-}
-
-/* listen or unlisten to a specific channel (multi-channel mode only) */
-static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen)
-{
- struct ohci_iso_recv *recv = iso->hostdata;
- int reg, i;
-
- if (channel < 32) {
- reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear;
- i = channel;
- } else {
- reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear;
- i = channel - 32;
- }
-
- reg_write(recv->ohci, reg, (1 << i));
-
- /* issue a dummy read to force all PCI writes to be posted immediately */
- mb();
- reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
-}
-
-static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
-{
- struct ohci_iso_recv *recv = iso->hostdata;
- int i;
-
- for (i = 0; i < 64; i++) {
- if (mask & (1ULL << i)) {
- if (i < 32)
- reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i));
- else
- reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32)));
- } else {
- if (i < 32)
- reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i));
- else
- reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32)));
- }
- }
-
- /* issue a dummy read to force all PCI writes to be posted immediately */
- mb();
- reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
-}
-
-static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
-{
- struct ohci_iso_recv *recv = iso->hostdata;
- struct ti_ohci *ohci = recv->ohci;
- u32 command, contextMatch;
-
- reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF);
- wmb();
-
- /* always keep ISO headers */
- command = (1 << 30);
-
- if (recv->dma_mode == BUFFER_FILL_MODE)
- command |= (1 << 31);
-
- reg_write(recv->ohci, recv->ContextControlSet, command);
-
- /* match on specified tags */
- contextMatch = tag_mask << 28;
-
- if (iso->channel == -1) {
- /* enable multichannel reception */
- reg_write(recv->ohci, recv->ContextControlSet, (1 << 28));
- } else {
- /* listen on channel */
- contextMatch |= iso->channel;
- }
-
- if (cycle != -1) {
- u32 seconds;
-
- /* enable cycleMatch */
- reg_write(recv->ohci, recv->ContextControlSet, (1 << 29));
-
- /* set starting cycle */
- cycle &= 0x1FFF;
-
- /* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
- just snarf them from the current time */
- seconds = reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
-
- /* advance one second to give some extra time for DMA to start */
- seconds += 1;
-
- cycle |= (seconds & 3) << 13;
-
- contextMatch |= cycle << 12;
- }
-
- if (sync != -1) {
- /* set sync flag on first DMA descriptor */
- struct dma_cmd *cmd = &recv->block[recv->block_dma];
- cmd->control |= cpu_to_le32(DMA_CTL_WAIT);
-
- /* match sync field */
- contextMatch |= (sync&0xf)<<8;
- }
-
- reg_write(recv->ohci, recv->ContextMatch, contextMatch);
-
- /* address of first descriptor block */
- command = dma_prog_region_offset_to_bus(&recv->prog,
- recv->block_dma * sizeof(struct dma_cmd));
- command |= 1; /* Z=1 */
-
- reg_write(recv->ohci, recv->CommandPtr, command);
-
- /* enable interrupts */
- reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << recv->task.context);
-
- wmb();
-
- /* run */
- reg_write(recv->ohci, recv->ContextControlSet, 0x8000);
-
- /* issue a dummy read of the cycle timer register to force
- all PCI writes to be posted immediately */
- mb();
- reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
-
- /* check RUN */
- if (!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) {
- PRINT(KERN_ERR,
- "Error starting IR DMA (ContextControl 0x%08x)\n",
- reg_read(recv->ohci, recv->ContextControlSet));
- return -1;
- }
-
- return 0;
-}
-
-static void ohci_iso_recv_release_block(struct ohci_iso_recv *recv, int block)
-{
- /* re-use the DMA descriptor for the block */
- /* by linking the previous descriptor to it */
-
- int next_i = block;
- int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1);
-
- struct dma_cmd *next = &recv->block[next_i];
- struct dma_cmd *prev = &recv->block[prev_i];
-
- /* ignore out-of-range requests */
- if ((block < 0) || (block > recv->nblocks))
- return;
-
- /* 'next' becomes the new end of the DMA chain,
- so disable branch and enable interrupt */
- next->branchAddress = 0;
- next->control |= cpu_to_le32(3 << 20);
- next->status = cpu_to_le32(recv->buf_stride);
-
- /* link prev to next */
- prev->branchAddress = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog,
- sizeof(struct dma_cmd) * next_i)
- | 1); /* Z=1 */
-
- /* disable interrupt on previous DMA descriptor, except at intervals */
- if ((prev_i % recv->block_irq_interval) == 0) {
- prev->control |= cpu_to_le32(3 << 20); /* enable interrupt */
- } else {
- prev->control &= cpu_to_le32(~(3<<20)); /* disable interrupt */
- }
- wmb();
-
- /* wake up DMA in case it fell asleep */
- reg_write(recv->ohci, recv->ContextControlSet, (1 << 12));
-}
-
-static void ohci_iso_recv_bufferfill_release(struct ohci_iso_recv *recv,
- struct hpsb_iso_packet_info *info)
-{
- /* release the memory where the packet was */
- recv->released_bytes += info->total_len;
-
- /* have we released enough memory for one block? */
- while (recv->released_bytes > recv->buf_stride) {
- ohci_iso_recv_release_block(recv, recv->block_reader);
- recv->block_reader = (recv->block_reader + 1) % recv->nblocks;
- recv->released_bytes -= recv->buf_stride;
- }
-}
-
-static inline void ohci_iso_recv_release(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
-{
- struct ohci_iso_recv *recv = iso->hostdata;
- if (recv->dma_mode == BUFFER_FILL_MODE) {
- ohci_iso_recv_bufferfill_release(recv, info);
- } else {
- ohci_iso_recv_release_block(recv, info - iso->infos);
- }
-}
-
-/* parse all packets from blocks that have been fully received */
-static void ohci_iso_recv_bufferfill_parse(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
-{
- int wake = 0;
- int runaway = 0;
- struct ti_ohci *ohci = recv->ohci;
-
- while (1) {
- /* we expect the next parsable packet to begin at recv->dma_offset */
- /* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */
-
- unsigned int offset;
- unsigned short len, cycle, total_len;
- unsigned char channel, tag, sy;
-
- unsigned char *p = iso->data_buf.kvirt;
-
- unsigned int this_block = recv->dma_offset/recv->buf_stride;
-
- /* don't loop indefinitely */
- if (runaway++ > 100000) {
- atomic_inc(&iso->overflows);
- PRINT(KERN_ERR,
- "IR DMA error - Runaway during buffer parsing!\n");
- break;
- }
-
- /* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */
- if (this_block == recv->block_dma)
- break;
-
- wake = 1;
-
- /* parse data length, tag, channel, and sy */
-
- /* note: we keep our own local copies of 'len' and 'offset'
- so the user can't mess with them by poking in the mmap area */
-
- len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8);
-
- if (len > 4096) {
- PRINT(KERN_ERR,
- "IR DMA error - bogus 'len' value %u\n", len);
- }
-
- channel = p[recv->dma_offset+1] & 0x3F;
- tag = p[recv->dma_offset+1] >> 6;
- sy = p[recv->dma_offset+0] & 0xF;
-
- /* advance to data payload */
- recv->dma_offset += 4;
-
- /* check for wrap-around */
- if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
- recv->dma_offset -= recv->buf_stride*recv->nblocks;
- }
-
- /* dma_offset now points to the first byte of the data payload */
- offset = recv->dma_offset;
-
- /* advance to xferStatus/timeStamp */
- recv->dma_offset += len;
-
- total_len = len + 8; /* 8 bytes header+trailer in OHCI packet */
- /* payload is padded to 4 bytes */
- if (len % 4) {
- recv->dma_offset += 4 - (len%4);
- total_len += 4 - (len%4);
- }
-
- /* check for wrap-around */
- if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
- /* uh oh, the packet data wraps from the last
- to the first DMA block - make the packet
- contiguous by copying its "tail" into the
- guard page */
-
- int guard_off = recv->buf_stride*recv->nblocks;
- int tail_len = len - (guard_off - offset);
-
- if (tail_len > 0 && tail_len < recv->buf_stride) {
- memcpy(iso->data_buf.kvirt + guard_off,
- iso->data_buf.kvirt,
- tail_len);
- }
-
- recv->dma_offset -= recv->buf_stride*recv->nblocks;
- }
-
- /* parse timestamp */
- cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8);
- cycle &= 0x1FFF;
-
- /* advance to next packet */
- recv->dma_offset += 4;
-
- /* check for wrap-around */
- if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
- recv->dma_offset -= recv->buf_stride*recv->nblocks;
- }
-
- hpsb_iso_packet_received(iso, offset, len, total_len, cycle, channel, tag, sy);
- }
-
- if (wake)
- hpsb_iso_wake(iso);
-}
-
-static void ohci_iso_recv_bufferfill_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
-{
- int loop;
- struct ti_ohci *ohci = recv->ohci;
-
- /* loop over all blocks */
- for (loop = 0; loop < recv->nblocks; loop++) {
-
- /* check block_dma to see if it's done */
- struct dma_cmd *im = &recv->block[recv->block_dma];
-
- /* check the DMA descriptor for new writes to xferStatus */
- u16 xferstatus = le32_to_cpu(im->status) >> 16;
-
- /* rescount is the number of bytes *remaining to be written* in the block */
- u16 rescount = le32_to_cpu(im->status) & 0xFFFF;
-
- unsigned char event = xferstatus & 0x1F;
-
- if (!event) {
- /* nothing has happened to this block yet */
- break;
- }
-
- if (event != 0x11) {
- atomic_inc(&iso->overflows);
- PRINT(KERN_ERR,
- "IR DMA error - OHCI error code 0x%02x\n", event);
- }
-
- if (rescount != 0) {
- /* the card is still writing to this block;
- we can't touch it until it's done */
- break;
- }
-
- /* OK, the block is finished... */
-
- /* sync our view of the block */
- dma_region_sync_for_cpu(&iso->data_buf, recv->block_dma*recv->buf_stride, recv->buf_stride);
-
- /* reset the DMA descriptor */
- im->status = recv->buf_stride;
-
- /* advance block_dma */
- recv->block_dma = (recv->block_dma + 1) % recv->nblocks;
-
- if ((recv->block_dma+1) % recv->nblocks == recv->block_reader) {
- atomic_inc(&iso->overflows);
- DBGMSG("ISO reception overflow - "
- "ran out of DMA blocks");
- }
- }
-
- /* parse any packets that have arrived */
- ohci_iso_recv_bufferfill_parse(iso, recv);
-}
-
-static void ohci_iso_recv_packetperbuf_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
-{
- int count;
- int wake = 0;
- struct ti_ohci *ohci = recv->ohci;
-
- /* loop over the entire buffer */
- for (count = 0; count < recv->nblocks; count++) {
- u32 packet_len = 0;
-
- /* pointer to the DMA descriptor */
- struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + iso->pkt_dma;
-
- /* check the DMA descriptor for new writes to xferStatus */
- u16 xferstatus = le32_to_cpu(il->status) >> 16;
- u16 rescount = le32_to_cpu(il->status) & 0xFFFF;
-
- unsigned char event = xferstatus & 0x1F;
-
- if (!event) {
- /* this packet hasn't come in yet; we are done for now */
- goto out;
- }
-
- if (event == 0x11) {
- /* packet received successfully! */
-
- /* rescount is the number of bytes *remaining* in the packet buffer,
- after the packet was written */
- packet_len = recv->buf_stride - rescount;
-
- } else if (event == 0x02) {
- PRINT(KERN_ERR, "IR DMA error - packet too long for buffer\n");
- } else if (event) {
- PRINT(KERN_ERR, "IR DMA error - OHCI error code 0x%02x\n", event);
- }
-
- /* sync our view of the buffer */
- dma_region_sync_for_cpu(&iso->data_buf, iso->pkt_dma * recv->buf_stride, recv->buf_stride);
-
- /* record the per-packet info */
- {
- /* iso header is 8 bytes ahead of the data payload */
- unsigned char *hdr;
-
- unsigned int offset;
- unsigned short cycle;
- unsigned char channel, tag, sy;
-
- offset = iso->pkt_dma * recv->buf_stride;
- hdr = iso->data_buf.kvirt + offset;
-
- /* skip iso header */
- offset += 8;
- packet_len -= 8;
-
- cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF;
- channel = hdr[5] & 0x3F;
- tag = hdr[5] >> 6;
- sy = hdr[4] & 0xF;
-
- hpsb_iso_packet_received(iso, offset, packet_len,
- recv->buf_stride, cycle, channel, tag, sy);
- }
-
- /* reset the DMA descriptor */
- il->status = recv->buf_stride;
-
- wake = 1;
- recv->block_dma = iso->pkt_dma;
- }
-
-out:
- if (wake)
- hpsb_iso_wake(iso);
-}
-
-static void ohci_iso_recv_task(unsigned long data)
-{
- struct hpsb_iso *iso = (struct hpsb_iso*) data;
- struct ohci_iso_recv *recv = iso->hostdata;
-
- if (recv->dma_mode == BUFFER_FILL_MODE)
- ohci_iso_recv_bufferfill_task(iso, recv);
- else
- ohci_iso_recv_packetperbuf_task(iso, recv);
-}
-
-/***********************************
- * rawiso ISO transmission *
- ***********************************/
-
-struct ohci_iso_xmit {
- struct ti_ohci *ohci;
- struct dma_prog_region prog;
- struct ohci1394_iso_tasklet task;
- int task_active;
- int last_cycle;
- atomic_t skips;
-
- u32 ContextControlSet;
- u32 ContextControlClear;
- u32 CommandPtr;
-};
-
-/* transmission DMA program:
- one OUTPUT_MORE_IMMEDIATE for the IT header
- one OUTPUT_LAST for the buffer data */
-
-struct iso_xmit_cmd {
- struct dma_cmd output_more_immediate;
- u8 iso_hdr[8];
- u32 unused[2];
- struct dma_cmd output_last;
-};
-
-static int ohci_iso_xmit_init(struct hpsb_iso *iso);
-static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle);
-static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso);
-static void ohci_iso_xmit_task(unsigned long data);
-
-static int ohci_iso_xmit_init(struct hpsb_iso *iso)
-{
- struct ohci_iso_xmit *xmit;
- unsigned int prog_size;
- int ctx;
- int ret = -ENOMEM;
-
- xmit = kmalloc(sizeof(*xmit), GFP_KERNEL);
- if (!xmit)
- return -ENOMEM;
-
- iso->hostdata = xmit;
- xmit->ohci = iso->host->hostdata;
- xmit->task_active = 0;
- xmit->last_cycle = -1;
- atomic_set(&iso->skips, 0);
-
- dma_prog_region_init(&xmit->prog);
-
- prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets;
-
- if (dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev))
- goto err;
-
- ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT,
- ohci_iso_xmit_task, (unsigned long) iso);
-
- if (ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) {
- ret = -EBUSY;
- goto err;
- }
-
- xmit->task_active = 1;
-
- /* xmit context registers are spaced 16 bytes apart */
- ctx = xmit->task.context;
- xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx;
- xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx;
- xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx;
-
- return 0;
-
-err:
- ohci_iso_xmit_shutdown(iso);
- return ret;
-}
-
-static void ohci_iso_xmit_stop(struct hpsb_iso *iso)
-{
- struct ohci_iso_xmit *xmit = iso->hostdata;
- struct ti_ohci *ohci = xmit->ohci;
-
- /* disable interrupts */
- reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context);
-
- /* halt DMA */
- if (ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) {
- /* XXX the DMA context will lock up if you try to send too much data! */
- PRINT(KERN_ERR,
- "you probably exceeded the OHCI card's bandwidth limit - "
- "reload the module and reduce xmit bandwidth");
- }
-}
-
-static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso)
-{
- struct ohci_iso_xmit *xmit = iso->hostdata;
-
- if (xmit->task_active) {
- ohci_iso_xmit_stop(iso);
- ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task);
- xmit->task_active = 0;
- }
-
- dma_prog_region_free(&xmit->prog);
- kfree(xmit);
- iso->hostdata = NULL;
-}
-
-static void ohci_iso_xmit_task(unsigned long data)
-{
- struct hpsb_iso *iso = (struct hpsb_iso*) data;
- struct ohci_iso_xmit *xmit = iso->hostdata;
- struct ti_ohci *ohci = xmit->ohci;
- int wake = 0;
- int count;
-
- /* check the whole buffer if necessary, starting at pkt_dma */
- for (count = 0; count < iso->buf_packets; count++) {
- int cycle;
-
- /* DMA descriptor */
- struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma);
-
- /* check for new writes to xferStatus */
- u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16;
- u8 event = xferstatus & 0x1F;
-
- if (!event) {
- /* packet hasn't been sent yet; we are done for now */
- break;
- }
-
- if (event != 0x11)
- PRINT(KERN_ERR,
- "IT DMA error - OHCI error code 0x%02x\n", event);
-
- /* at least one packet went out, so wake up the writer */
- wake = 1;
-
- /* parse cycle */
- cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF;
-
- if (xmit->last_cycle > -1) {
- int cycle_diff = cycle - xmit->last_cycle;
- int skip;
-
- /* unwrap */
- if (cycle_diff < 0) {
- cycle_diff += 8000;
- if (cycle_diff < 0)
- PRINT(KERN_ERR, "bogus cycle diff %d\n",
- cycle_diff);
- }
-
- skip = cycle_diff - 1;
- if (skip > 0) {
- DBGMSG("skipped %d cycles without packet loss", skip);
- atomic_add(skip, &iso->skips);
- }
- }
- xmit->last_cycle = cycle;
-
- /* tell the subsystem the packet has gone out */
- hpsb_iso_packet_sent(iso, cycle, event != 0x11);
-
- /* reset the DMA descriptor for next time */
- cmd->output_last.status = 0;
- }
-
- if (wake)
- hpsb_iso_wake(iso);
-}
-
-static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
-{
- struct ohci_iso_xmit *xmit = iso->hostdata;
- struct ti_ohci *ohci = xmit->ohci;
-
- int next_i, prev_i;
- struct iso_xmit_cmd *next, *prev;
-
- unsigned int offset;
- unsigned short len;
- unsigned char tag, sy;
-
- /* check that the packet doesn't cross a page boundary
- (we could allow this if we added OUTPUT_MORE descriptor support) */
- if (cross_bound(info->offset, info->len)) {
- PRINT(KERN_ERR,
- "rawiso xmit: packet %u crosses a page boundary",
- iso->first_packet);
- return -EINVAL;
- }
-
- offset = info->offset;
- len = info->len;
- tag = info->tag;
- sy = info->sy;
-
- /* sync up the card's view of the buffer */
- dma_region_sync_for_device(&iso->data_buf, offset, len);
-
- /* append first_packet to the DMA chain */
- /* by linking the previous descriptor to it */
- /* (next will become the new end of the DMA chain) */
-
- next_i = iso->first_packet;
- prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1);
-
- next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i);
- prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i);
-
- /* set up the OUTPUT_MORE_IMMEDIATE descriptor */
- memset(next, 0, sizeof(struct iso_xmit_cmd));
- next->output_more_immediate.control = cpu_to_le32(0x02000008);
-
- /* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */
-
- /* tcode = 0xA, and sy */
- next->iso_hdr[0] = 0xA0 | (sy & 0xF);
-
- /* tag and channel number */
- next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F);
-
- /* transmission speed */
- next->iso_hdr[2] = iso->speed & 0x7;
-
- /* payload size */
- next->iso_hdr[6] = len & 0xFF;
- next->iso_hdr[7] = len >> 8;
-
- /* set up the OUTPUT_LAST */
- next->output_last.control = cpu_to_le32(1 << 28);
- next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */
- next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */
- next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */
- next->output_last.control |= cpu_to_le32(len);
-
- /* payload bus address */
- next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset));
-
- /* leave branchAddress at zero for now */
-
- /* re-write the previous DMA descriptor to chain to this one */
-
- /* set prev branch address to point to next (Z=3) */
- prev->output_last.branchAddress = cpu_to_le32(
- dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3);
-
- /*
- * Link the skip address to this descriptor itself. This causes a
- * context to skip a cycle whenever lost cycles or FIFO overruns occur,
- * without dropping the data at that point the application should then
- * decide whether this is an error condition or not. Some protocols
- * can deal with this by dropping some rate-matching padding packets.
- */
- next->output_more_immediate.branchAddress =
- prev->output_last.branchAddress;
-
- /* disable interrupt, unless required by the IRQ interval */
- if (prev_i % iso->irq_interval) {
- prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */
- } else {
- prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */
- }
-
- wmb();
-
- /* wake DMA in case it is sleeping */
- reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12);
-
- /* issue a dummy read of the cycle timer to force all PCI
- writes to be posted immediately */
- mb();
- reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer);
-
- return 0;
-}
-
-static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle)
-{
- struct ohci_iso_xmit *xmit = iso->hostdata;
- struct ti_ohci *ohci = xmit->ohci;
-
- /* clear out the control register */
- reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF);
- wmb();
-
- /* address and length of first descriptor block (Z=3) */
- reg_write(xmit->ohci, xmit->CommandPtr,
- dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3);
-
- /* cycle match */
- if (cycle != -1) {
- u32 start = cycle & 0x1FFF;
-
- /* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
- just snarf them from the current time */
- u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
-
- /* advance one second to give some extra time for DMA to start */
- seconds += 1;
-
- start |= (seconds & 3) << 13;
-
- reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16));
- }
-
- /* enable interrupts */
- reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context);
-
- /* run */
- reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000);
- mb();
-
- /* wait 100 usec to give the card time to go active */
- udelay(100);
-
- /* check the RUN bit */
- if (!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) {
- PRINT(KERN_ERR, "Error starting IT DMA (ContextControl 0x%08x)\n",
- reg_read(xmit->ohci, xmit->ContextControlSet));
- return -1;
- }
-
- return 0;
-}
-
-static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg)
-{
-
- switch(cmd) {
- case XMIT_INIT:
- return ohci_iso_xmit_init(iso);
- case XMIT_START:
- return ohci_iso_xmit_start(iso, arg);
- case XMIT_STOP:
- ohci_iso_xmit_stop(iso);
- return 0;
- case XMIT_QUEUE:
- return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg);
- case XMIT_SHUTDOWN:
- ohci_iso_xmit_shutdown(iso);
- return 0;
-
- case RECV_INIT:
- return ohci_iso_recv_init(iso);
- case RECV_START: {
- int *args = (int*) arg;
- return ohci_iso_recv_start(iso, args[0], args[1], args[2]);
- }
- case RECV_STOP:
- ohci_iso_recv_stop(iso);
- return 0;
- case RECV_RELEASE:
- ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg);
- return 0;
- case RECV_FLUSH:
- ohci_iso_recv_task((unsigned long) iso);
- return 0;
- case RECV_SHUTDOWN:
- ohci_iso_recv_shutdown(iso);
- return 0;
- case RECV_LISTEN_CHANNEL:
- ohci_iso_recv_change_channel(iso, arg, 1);
- return 0;
- case RECV_UNLISTEN_CHANNEL:
- ohci_iso_recv_change_channel(iso, arg, 0);
- return 0;
- case RECV_SET_CHANNEL_MASK:
- ohci_iso_recv_set_channel_mask(iso, *((u64*) arg));
- return 0;
-
- default:
- PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet",
- cmd);
- break;
- }
- return -EINVAL;
-}
-
-/***************************************
- * IEEE-1394 functionality section END *
- ***************************************/
-
-
-/********************************************************
- * Global stuff (interrupt handler, init/shutdown code) *
- ********************************************************/
-
-static void dma_trm_reset(struct dma_trm_ctx *d)
-{
- unsigned long flags;
- LIST_HEAD(packet_list);
- struct ti_ohci *ohci = d->ohci;
- struct hpsb_packet *packet, *ptmp;
-
- ohci1394_stop_context(ohci, d->ctrlClear, NULL);
-
- /* Lock the context, reset it and release it. Move the packets
- * that were pending in the context to packet_list and free
- * them after releasing the lock. */
-
- spin_lock_irqsave(&d->lock, flags);
-
- list_splice_init(&d->fifo_list, &packet_list);
- list_splice_init(&d->pending_list, &packet_list);
-
- d->branchAddrPtr = NULL;
- d->sent_ind = d->prg_ind;
- d->free_prgs = d->num_desc;
-
- spin_unlock_irqrestore(&d->lock, flags);
-
- if (list_empty(&packet_list))
- return;
-
- PRINT(KERN_INFO, "AT dma reset ctx=%d, aborting transmission", d->ctx);
-
- /* Now process subsystem callbacks for the packets from this
- * context. */
- list_for_each_entry_safe(packet, ptmp, &packet_list, driver_list) {
- list_del_init(&packet->driver_list);
- hpsb_packet_sent(ohci->host, packet, ACKX_ABORTED);
- }
-}
-
-static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci,
- quadlet_t rx_event,
- quadlet_t tx_event)
-{
- struct ohci1394_iso_tasklet *t;
- unsigned long mask;
- unsigned long flags;
-
- spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
-
- list_for_each_entry(t, &ohci->iso_tasklet_list, link) {
- mask = 1 << t->context;
-
- if (t->type == OHCI_ISO_TRANSMIT) {
- if (tx_event & mask)
- tasklet_schedule(&t->tasklet);
- } else {
- /* OHCI_ISO_RECEIVE or OHCI_ISO_MULTICHANNEL_RECEIVE */
- if (rx_event & mask)
- tasklet_schedule(&t->tasklet);
- }
- }
-
- spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
-}
-
-static irqreturn_t ohci_irq_handler(int irq, void *dev_id)
-{
- quadlet_t event, node_id;
- struct ti_ohci *ohci = (struct ti_ohci *)dev_id;
- struct hpsb_host *host = ohci->host;
- int phyid = -1, isroot = 0;
- unsigned long flags;
-
- /* Read and clear the interrupt event register. Don't clear
- * the busReset event, though. This is done when we get the
- * selfIDComplete interrupt. */
- spin_lock_irqsave(&ohci->event_lock, flags);
- event = reg_read(ohci, OHCI1394_IntEventClear);
- reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset);
- spin_unlock_irqrestore(&ohci->event_lock, flags);
-
- if (!event)
- return IRQ_NONE;
-
- /* If event is ~(u32)0 cardbus card was ejected. In this case
- * we just return, and clean up in the ohci1394_pci_remove
- * function. */
- if (event == ~(u32) 0) {
- DBGMSG("Device removed.");
- return IRQ_NONE;
- }
-
- DBGMSG("IntEvent: %08x", event);
-
- if (event & OHCI1394_unrecoverableError) {
- int ctx;
- PRINT(KERN_ERR, "Unrecoverable error!");
-
- if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800)
- PRINT(KERN_ERR, "Async Req Tx Context died: "
- "ctrl[%08x] cmdptr[%08x]",
- reg_read(ohci, OHCI1394_AsReqTrContextControlSet),
- reg_read(ohci, OHCI1394_AsReqTrCommandPtr));
-
- if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800)
- PRINT(KERN_ERR, "Async Rsp Tx Context died: "
- "ctrl[%08x] cmdptr[%08x]",
- reg_read(ohci, OHCI1394_AsRspTrContextControlSet),
- reg_read(ohci, OHCI1394_AsRspTrCommandPtr));
-
- if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800)
- PRINT(KERN_ERR, "Async Req Rcv Context died: "
- "ctrl[%08x] cmdptr[%08x]",
- reg_read(ohci, OHCI1394_AsReqRcvContextControlSet),
- reg_read(ohci, OHCI1394_AsReqRcvCommandPtr));
-
- if (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x800)
- PRINT(KERN_ERR, "Async Rsp Rcv Context died: "
- "ctrl[%08x] cmdptr[%08x]",
- reg_read(ohci, OHCI1394_AsRspRcvContextControlSet),
- reg_read(ohci, OHCI1394_AsRspRcvCommandPtr));
-
- for (ctx = 0; ctx < ohci->nb_iso_xmit_ctx; ctx++) {
- if (reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)) & 0x800)
- PRINT(KERN_ERR, "Iso Xmit %d Context died: "
- "ctrl[%08x] cmdptr[%08x]", ctx,
- reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)),
- reg_read(ohci, OHCI1394_IsoXmitCommandPtr + (16 * ctx)));
- }
-
- for (ctx = 0; ctx < ohci->nb_iso_rcv_ctx; ctx++) {
- if (reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)) & 0x800)
- PRINT(KERN_ERR, "Iso Recv %d Context died: "
- "ctrl[%08x] cmdptr[%08x] match[%08x]", ctx,
- reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)),
- reg_read(ohci, OHCI1394_IsoRcvCommandPtr + (32 * ctx)),
- reg_read(ohci, OHCI1394_IsoRcvContextMatch + (32 * ctx)));
- }
-
- event &= ~OHCI1394_unrecoverableError;
- }
- if (event & OHCI1394_postedWriteErr) {
- PRINT(KERN_ERR, "physical posted write error");
- /* no recovery strategy yet, had to involve protocol drivers */
- event &= ~OHCI1394_postedWriteErr;
- }
- if (event & OHCI1394_cycleTooLong) {
- if(printk_ratelimit())
- PRINT(KERN_WARNING, "isochronous cycle too long");
- else
- DBGMSG("OHCI1394_cycleTooLong");
- reg_write(ohci, OHCI1394_LinkControlSet,
- OHCI1394_LinkControl_CycleMaster);
- event &= ~OHCI1394_cycleTooLong;
- }
- if (event & OHCI1394_cycleInconsistent) {
- /* We subscribe to the cycleInconsistent event only to
- * clear the corresponding event bit... otherwise,
- * isochronous cycleMatch DMA won't work. */
- DBGMSG("OHCI1394_cycleInconsistent");
- event &= ~OHCI1394_cycleInconsistent;
- }
- if (event & OHCI1394_busReset) {
- /* The busReset event bit can't be cleared during the
- * selfID phase, so we disable busReset interrupts, to
- * avoid burying the cpu in interrupt requests. */
- spin_lock_irqsave(&ohci->event_lock, flags);
- reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
-
- if (ohci->check_busreset) {
- int loop_count = 0;
-
- udelay(10);
-
- while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
- reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
-
- spin_unlock_irqrestore(&ohci->event_lock, flags);
- udelay(10);
- spin_lock_irqsave(&ohci->event_lock, flags);
-
- /* The loop counter check is to prevent the driver
- * from remaining in this state forever. For the
- * initial bus reset, the loop continues for ever
- * and the system hangs, until some device is plugged-in
- * or out manually into a port! The forced reset seems
- * to solve this problem. This mainly effects nForce2. */
- if (loop_count > 10000) {
- ohci_devctl(host, RESET_BUS, LONG_RESET);
- DBGMSG("Detected bus-reset loop. Forced a bus reset!");
- loop_count = 0;
- }
-
- loop_count++;
- }
- }
- spin_unlock_irqrestore(&ohci->event_lock, flags);
- if (!host->in_bus_reset) {
- DBGMSG("irq_handler: Bus reset requested");
-
- /* Subsystem call */
- hpsb_bus_reset(ohci->host);
- }
- event &= ~OHCI1394_busReset;
- }
- if (event & OHCI1394_reqTxComplete) {
- struct dma_trm_ctx *d = &ohci->at_req_context;
- DBGMSG("Got reqTxComplete interrupt "
- "status=0x%08X", reg_read(ohci, d->ctrlSet));
- if (reg_read(ohci, d->ctrlSet) & 0x800)
- ohci1394_stop_context(ohci, d->ctrlClear,
- "reqTxComplete");
- else
- dma_trm_tasklet((unsigned long)d);
- //tasklet_schedule(&d->task);
- event &= ~OHCI1394_reqTxComplete;
- }
- if (event & OHCI1394_respTxComplete) {
- struct dma_trm_ctx *d = &ohci->at_resp_context;
- DBGMSG("Got respTxComplete interrupt "
- "status=0x%08X", reg_read(ohci, d->ctrlSet));
- if (reg_read(ohci, d->ctrlSet) & 0x800)
- ohci1394_stop_context(ohci, d->ctrlClear,
- "respTxComplete");
- else
- tasklet_schedule(&d->task);
- event &= ~OHCI1394_respTxComplete;
- }
- if (event & OHCI1394_RQPkt) {
- struct dma_rcv_ctx *d = &ohci->ar_req_context;
- DBGMSG("Got RQPkt interrupt status=0x%08X",
- reg_read(ohci, d->ctrlSet));
- if (reg_read(ohci, d->ctrlSet) & 0x800)
- ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt");
- else
- tasklet_schedule(&d->task);
- event &= ~OHCI1394_RQPkt;
- }
- if (event & OHCI1394_RSPkt) {
- struct dma_rcv_ctx *d = &ohci->ar_resp_context;
- DBGMSG("Got RSPkt interrupt status=0x%08X",
- reg_read(ohci, d->ctrlSet));
- if (reg_read(ohci, d->ctrlSet) & 0x800)
- ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt");
- else
- tasklet_schedule(&d->task);
- event &= ~OHCI1394_RSPkt;
- }
- if (event & OHCI1394_isochRx) {
- quadlet_t rx_event;
-
- rx_event = reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
- reg_write(ohci, OHCI1394_IsoRecvIntEventClear, rx_event);
- ohci_schedule_iso_tasklets(ohci, rx_event, 0);
- event &= ~OHCI1394_isochRx;
- }
- if (event & OHCI1394_isochTx) {
- quadlet_t tx_event;
-
- tx_event = reg_read(ohci, OHCI1394_IsoXmitIntEventSet);
- reg_write(ohci, OHCI1394_IsoXmitIntEventClear, tx_event);
- ohci_schedule_iso_tasklets(ohci, 0, tx_event);
- event &= ~OHCI1394_isochTx;
- }
- if (event & OHCI1394_selfIDComplete) {
- if (host->in_bus_reset) {
- node_id = reg_read(ohci, OHCI1394_NodeID);
-
- if (!(node_id & 0x80000000)) {
- PRINT(KERN_ERR,
- "SelfID received, but NodeID invalid "
- "(probably new bus reset occurred): %08X",
- node_id);
- goto selfid_not_valid;
- }
-
- phyid = node_id & 0x0000003f;
- isroot = (node_id & 0x40000000) != 0;
-
- DBGMSG("SelfID interrupt received "
- "(phyid %d, %s)", phyid,
- (isroot ? "root" : "not root"));
-
- handle_selfid(ohci, host, phyid, isroot);
-
- /* Clear the bus reset event and re-enable the
- * busReset interrupt. */
- spin_lock_irqsave(&ohci->event_lock, flags);
- reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
- reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset);
- spin_unlock_irqrestore(&ohci->event_lock, flags);
-
- /* Turn on phys dma reception.
- *
- * TODO: Enable some sort of filtering management.
- */
- if (phys_dma) {
- reg_write(ohci, OHCI1394_PhyReqFilterHiSet,
- 0xffffffff);
- reg_write(ohci, OHCI1394_PhyReqFilterLoSet,
- 0xffffffff);
- }
-
- DBGMSG("PhyReqFilter=%08x%08x",
- reg_read(ohci, OHCI1394_PhyReqFilterHiSet),
- reg_read(ohci, OHCI1394_PhyReqFilterLoSet));
-
- hpsb_selfid_complete(host, phyid, isroot);
- } else
- PRINT(KERN_ERR,
- "SelfID received outside of bus reset sequence");
-
-selfid_not_valid:
- event &= ~OHCI1394_selfIDComplete;
- }
-
- /* Make sure we handle everything, just in case we accidentally
- * enabled an interrupt that we didn't write a handler for. */
- if (event)
- PRINT(KERN_ERR, "Unhandled interrupt(s) 0x%08x",
- event);
-
- return IRQ_HANDLED;
-}
-
-/* Put the buffer back into the dma context */
-static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx)
-{
- struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
- DBGMSG("Inserting dma buf ctx=%d idx=%d", d->ctx, idx);
-
- d->prg_cpu[idx]->status = cpu_to_le32(d->buf_size);
- d->prg_cpu[idx]->branchAddress &= le32_to_cpu(0xfffffff0);
- idx = (idx + d->num_desc - 1 ) % d->num_desc;
- d->prg_cpu[idx]->branchAddress |= le32_to_cpu(0x00000001);
-
- /* To avoid a race, ensure 1394 interface hardware sees the inserted
- * context program descriptors before it sees the wakeup bit set. */
- wmb();
-
- /* wake up the dma context if necessary */
- if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
- PRINT(KERN_INFO,
- "Waking dma ctx=%d ... processing is probably too slow",
- d->ctx);
- }
-
- /* do this always, to avoid race condition */
- reg_write(ohci, d->ctrlSet, 0x1000);
-}
-
-#define cond_le32_to_cpu(data, noswap) \
- (noswap ? data : le32_to_cpu(data))
-
-static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0,
- -1, 0, -1, 0, -1, -1, 16, -1};
-
-/*
- * Determine the length of a packet in the buffer
- * Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca>
- */
-static inline int packet_length(struct dma_rcv_ctx *d, int idx,
- quadlet_t *buf_ptr, int offset,
- unsigned char tcode, int noswap)
-{
- int length = -1;
-
- if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) {
- length = TCODE_SIZE[tcode];
- if (length == 0) {
- if (offset + 12 >= d->buf_size) {
- length = (cond_le32_to_cpu(d->buf_cpu[(idx + 1) % d->num_desc]
- [3 - ((d->buf_size - offset) >> 2)], noswap) >> 16);
- } else {
- length = (cond_le32_to_cpu(buf_ptr[3], noswap) >> 16);
- }
- length += 20;
- }
- } else if (d->type == DMA_CTX_ISO) {
- /* Assumption: buffer fill mode with header/trailer */
- length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8;
- }
-
- if (length > 0 && length % 4)
- length += 4 - (length % 4);
-
- return length;
-}
-
-/* Tasklet that processes dma receive buffers */
-static void dma_rcv_tasklet (unsigned long data)
-{
- struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data;
- struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
- unsigned int split_left, idx, offset, rescount;
- unsigned char tcode;
- int length, bytes_left, ack;
- unsigned long flags;
- quadlet_t *buf_ptr;
- char *split_ptr;
- char msg[256];
-
- spin_lock_irqsave(&d->lock, flags);
-
- idx = d->buf_ind;
- offset = d->buf_offset;
- buf_ptr = d->buf_cpu[idx] + offset/4;
-
- rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
- bytes_left = d->buf_size - rescount - offset;
-
- while (bytes_left > 0) {
- tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf;
-
- /* packet_length() will return < 4 for an error */
- length = packet_length(d, idx, buf_ptr, offset, tcode, ohci->no_swap_incoming);
-
- if (length < 4) { /* something is wrong */
- sprintf(msg,"Unexpected tcode 0x%x(0x%08x) in AR ctx=%d, length=%d",
- tcode, cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming),
- d->ctx, length);
- ohci1394_stop_context(ohci, d->ctrlClear, msg);
- spin_unlock_irqrestore(&d->lock, flags);
- return;
- }
-
- /* The first case is where we have a packet that crosses
- * over more than one descriptor. The next case is where
- * it's all in the first descriptor. */
- if ((offset + length) > d->buf_size) {
- DBGMSG("Split packet rcv'd");
- if (length > d->split_buf_size) {
- ohci1394_stop_context(ohci, d->ctrlClear,
- "Split packet size exceeded");
- d->buf_ind = idx;
- d->buf_offset = offset;
- spin_unlock_irqrestore(&d->lock, flags);
- return;
- }
-
- if (le32_to_cpu(d->prg_cpu[(idx+1)%d->num_desc]->status)
- == d->buf_size) {
- /* Other part of packet not written yet.
- * this should never happen I think
- * anyway we'll get it on the next call. */
- PRINT(KERN_INFO,
- "Got only half a packet!");
- d->buf_ind = idx;
- d->buf_offset = offset;
- spin_unlock_irqrestore(&d->lock, flags);
- return;
- }
-
- split_left = length;
- split_ptr = (char *)d->spb;
- memcpy(split_ptr,buf_ptr,d->buf_size-offset);
- split_left -= d->buf_size-offset;
- split_ptr += d->buf_size-offset;
- insert_dma_buffer(d, idx);
- idx = (idx+1) % d->num_desc;
- buf_ptr = d->buf_cpu[idx];
- offset=0;
-
- while (split_left >= d->buf_size) {
- memcpy(split_ptr,buf_ptr,d->buf_size);
- split_ptr += d->buf_size;
- split_left -= d->buf_size;
- insert_dma_buffer(d, idx);
- idx = (idx+1) % d->num_desc;
- buf_ptr = d->buf_cpu[idx];
- }
-
- if (split_left > 0) {
- memcpy(split_ptr, buf_ptr, split_left);
- offset = split_left;
- buf_ptr += offset/4;
- }
- } else {
- DBGMSG("Single packet rcv'd");
- memcpy(d->spb, buf_ptr, length);
- offset += length;
- buf_ptr += length/4;
- if (offset==d->buf_size) {
- insert_dma_buffer(d, idx);
- idx = (idx+1) % d->num_desc;
- buf_ptr = d->buf_cpu[idx];
- offset=0;
- }
- }
-
- /* We get one phy packet to the async descriptor for each
- * bus reset. We always ignore it. */
- if (tcode != OHCI1394_TCODE_PHY) {
- if (!ohci->no_swap_incoming)
- header_le32_to_cpu(d->spb, tcode);
- DBGMSG("Packet received from node"
- " %d ack=0x%02X spd=%d tcode=0x%X"
- " length=%d ctx=%d tlabel=%d",
- (d->spb[1]>>16)&0x3f,
- (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f,
- (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3,
- tcode, length, d->ctx,
- (d->spb[0]>>10)&0x3f);
-
- ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f)
- == 0x11) ? 1 : 0;
-
- hpsb_packet_received(ohci->host, d->spb,
- length-4, ack);
- }
-#ifdef OHCI1394_DEBUG
- else
- PRINT (KERN_DEBUG, "Got phy packet ctx=%d ... discarded",
- d->ctx);
-#endif
-
- rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
-
- bytes_left = d->buf_size - rescount - offset;
-
- }
-
- d->buf_ind = idx;
- d->buf_offset = offset;
-
- spin_unlock_irqrestore(&d->lock, flags);
-}
-
-/* Bottom half that processes sent packets */
-static void dma_trm_tasklet (unsigned long data)
-{
- struct dma_trm_ctx *d = (struct dma_trm_ctx*)data;
- struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
- struct hpsb_packet *packet, *ptmp;
- unsigned long flags;
- u32 status, ack;
- size_t datasize;
-
- spin_lock_irqsave(&d->lock, flags);
-
- list_for_each_entry_safe(packet, ptmp, &d->fifo_list, driver_list) {
- datasize = packet->data_size;
- if (datasize && packet->type != hpsb_raw)
- status = le32_to_cpu(
- d->prg_cpu[d->sent_ind]->end.status) >> 16;
- else
- status = le32_to_cpu(
- d->prg_cpu[d->sent_ind]->begin.status) >> 16;
-
- if (status == 0)
- /* this packet hasn't been sent yet*/
- break;
-
-#ifdef OHCI1394_DEBUG
- if (datasize)
- if (((le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf) == 0xa)
- DBGMSG("Stream packet sent to channel %d tcode=0x%X "
- "ack=0x%X spd=%d dataLength=%d ctx=%d",
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>8)&0x3f,
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf,
- status&0x1f, (status>>5)&0x3,
- le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16,
- d->ctx);
- else
- DBGMSG("Packet sent to node %d tcode=0x%X tLabel="
- "%d ack=0x%X spd=%d dataLength=%d ctx=%d",
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16)&0x3f,
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf,
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>10)&0x3f,
- status&0x1f, (status>>5)&0x3,
- le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3])>>16,
- d->ctx);
- else
- DBGMSG("Packet sent to node %d tcode=0x%X tLabel="
- "%d ack=0x%X spd=%d data=0x%08X ctx=%d",
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])
- >>16)&0x3f,
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])
- >>4)&0xf,
- (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])
- >>10)&0x3f,
- status&0x1f, (status>>5)&0x3,
- le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3]),
- d->ctx);
-#endif
-
- if (status & 0x10) {
- ack = status & 0xf;
- } else {
- switch (status & 0x1f) {
- case EVT_NO_STATUS: /* that should never happen */
- case EVT_RESERVED_A: /* that should never happen */
- case EVT_LONG_PACKET: /* that should never happen */
- PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
- ack = ACKX_SEND_ERROR;
- break;
- case EVT_MISSING_ACK:
- ack = ACKX_TIMEOUT;
- break;
- case EVT_UNDERRUN:
- ack = ACKX_SEND_ERROR;
- break;
- case EVT_OVERRUN: /* that should never happen */
- PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
- ack = ACKX_SEND_ERROR;
- break;
- case EVT_DESCRIPTOR_READ:
- case EVT_DATA_READ:
- case EVT_DATA_WRITE:
- ack = ACKX_SEND_ERROR;
- break;
- case EVT_BUS_RESET: /* that should never happen */
- PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
- ack = ACKX_SEND_ERROR;
- break;
- case EVT_TIMEOUT:
- ack = ACKX_TIMEOUT;
- break;
- case EVT_TCODE_ERR:
- ack = ACKX_SEND_ERROR;
- break;
- case EVT_RESERVED_B: /* that should never happen */
- case EVT_RESERVED_C: /* that should never happen */
- PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
- ack = ACKX_SEND_ERROR;
- break;
- case EVT_UNKNOWN:
- case EVT_FLUSHED:
- ack = ACKX_SEND_ERROR;
- break;
- default:
- PRINT(KERN_ERR, "Unhandled OHCI evt_* error 0x%x", status & 0x1f);
- ack = ACKX_SEND_ERROR;
- BUG();
- }
- }
-
- list_del_init(&packet->driver_list);
- hpsb_packet_sent(ohci->host, packet, ack);
-
- if (datasize)
- pci_unmap_single(ohci->dev,
- cpu_to_le32(d->prg_cpu[d->sent_ind]->end.address),
- datasize, PCI_DMA_TODEVICE);
-
- d->sent_ind = (d->sent_ind+1)%d->num_desc;
- d->free_prgs++;
- }
-
- dma_trm_flush(ohci, d);
-
- spin_unlock_irqrestore(&d->lock, flags);
-}
-
-static void free_dma_rcv_ctx(struct dma_rcv_ctx *d)
-{
- int i;
- struct ti_ohci *ohci = d->ohci;
-
- if (ohci == NULL)
- return;
-
- DBGMSG("Freeing dma_rcv_ctx %d", d->ctx);
-
- if (d->buf_cpu) {
- for (i=0; i<d->num_desc; i++)
- if (d->buf_cpu[i] && d->buf_bus[i])
- pci_free_consistent(
- ohci->dev, d->buf_size,
- d->buf_cpu[i], d->buf_bus[i]);
- kfree(d->buf_cpu);
- kfree(d->buf_bus);
- }
- if (d->prg_cpu) {
- for (i=0; i<d->num_desc; i++)
- if (d->prg_cpu[i] && d->prg_bus[i])
- pci_pool_free(d->prg_pool, d->prg_cpu[i],
- d->prg_bus[i]);
- pci_pool_destroy(d->prg_pool);
- kfree(d->prg_cpu);
- kfree(d->prg_bus);
- }
- kfree(d->spb);
-
- /* Mark this context as freed. */
- d->ohci = NULL;
-}
-
-static int
-alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
- enum context_type type, int ctx, int num_desc,
- int buf_size, int split_buf_size, int context_base)
-{
- int i, len;
- static int num_allocs;
- static char pool_name[20];
-
- d->ohci = ohci;
- d->type = type;
- d->ctx = ctx;
-
- d->num_desc = num_desc;
- d->buf_size = buf_size;
- d->split_buf_size = split_buf_size;
-
- d->ctrlSet = 0;
- d->ctrlClear = 0;
- d->cmdPtr = 0;
-
- d->buf_cpu = kzalloc(d->num_desc * sizeof(*d->buf_cpu), GFP_ATOMIC);
- d->buf_bus = kzalloc(d->num_desc * sizeof(*d->buf_bus), GFP_ATOMIC);
-
- if (d->buf_cpu == NULL || d->buf_bus == NULL) {
- PRINT(KERN_ERR, "Failed to allocate %s", "DMA buffer");
- free_dma_rcv_ctx(d);
- return -ENOMEM;
- }
-
- d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_ATOMIC);
- d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_ATOMIC);
-
- if (d->prg_cpu == NULL || d->prg_bus == NULL) {
- PRINT(KERN_ERR, "Failed to allocate %s", "DMA prg");
- free_dma_rcv_ctx(d);
- return -ENOMEM;
- }
-
- d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC);
-
- if (d->spb == NULL) {
- PRINT(KERN_ERR, "Failed to allocate %s", "split buffer");
- free_dma_rcv_ctx(d);
- return -ENOMEM;
- }
-
- len = sprintf(pool_name, "ohci1394_rcv_prg");
- sprintf(pool_name+len, "%d", num_allocs);
- d->prg_pool = pci_pool_create(pool_name, ohci->dev,
- sizeof(struct dma_cmd), 4, 0);
- if(d->prg_pool == NULL)
- {
- PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name);
- free_dma_rcv_ctx(d);
- return -ENOMEM;
- }
- num_allocs++;
-
- for (i=0; i<d->num_desc; i++) {
- d->buf_cpu[i] = pci_alloc_consistent(ohci->dev,
- d->buf_size,
- d->buf_bus+i);
-
- if (d->buf_cpu[i] != NULL) {
- memset(d->buf_cpu[i], 0, d->buf_size);
- } else {
- PRINT(KERN_ERR,
- "Failed to allocate %s", "DMA buffer");
- free_dma_rcv_ctx(d);
- return -ENOMEM;
- }
-
- d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i);
-
- if (d->prg_cpu[i] != NULL) {
- memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd));
- } else {
- PRINT(KERN_ERR,
- "Failed to allocate %s", "DMA prg");
- free_dma_rcv_ctx(d);
- return -ENOMEM;
- }
- }
-
- spin_lock_init(&d->lock);
-
- d->ctrlSet = context_base + OHCI1394_ContextControlSet;
- d->ctrlClear = context_base + OHCI1394_ContextControlClear;
- d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
-
- tasklet_init(&d->task, dma_rcv_tasklet, (unsigned long) d);
- return 0;
-}
-
-static void free_dma_trm_ctx(struct dma_trm_ctx *d)
-{
- int i;
- struct ti_ohci *ohci = d->ohci;
-
- if (ohci == NULL)
- return;
-
- DBGMSG("Freeing dma_trm_ctx %d", d->ctx);
-
- if (d->prg_cpu) {
- for (i=0; i<d->num_desc; i++)
- if (d->prg_cpu[i] && d->prg_bus[i])
- pci_pool_free(d->prg_pool, d->prg_cpu[i],
- d->prg_bus[i]);
- pci_pool_destroy(d->prg_pool);
- kfree(d->prg_cpu);
- kfree(d->prg_bus);
- }
-
- /* Mark this context as freed. */
- d->ohci = NULL;
-}
-
-static int
-alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
- enum context_type type, int ctx, int num_desc,
- int context_base)
-{
- int i, len;
- static char pool_name[20];
- static int num_allocs=0;
-
- d->ohci = ohci;
- d->type = type;
- d->ctx = ctx;
- d->num_desc = num_desc;
- d->ctrlSet = 0;
- d->ctrlClear = 0;
- d->cmdPtr = 0;
-
- d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_KERNEL);
- d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_KERNEL);
-
- if (d->prg_cpu == NULL || d->prg_bus == NULL) {
- PRINT(KERN_ERR, "Failed to allocate %s", "AT DMA prg");
- free_dma_trm_ctx(d);
- return -ENOMEM;
- }
-
- len = sprintf(pool_name, "ohci1394_trm_prg");
- sprintf(pool_name+len, "%d", num_allocs);
- d->prg_pool = pci_pool_create(pool_name, ohci->dev,
- sizeof(struct at_dma_prg), 4, 0);
- if (d->prg_pool == NULL) {
- PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name);
- free_dma_trm_ctx(d);
- return -ENOMEM;
- }
- num_allocs++;
-
- for (i = 0; i < d->num_desc; i++) {
- d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i);
-
- if (d->prg_cpu[i] != NULL) {
- memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg));
- } else {
- PRINT(KERN_ERR,
- "Failed to allocate %s", "AT DMA prg");
- free_dma_trm_ctx(d);
- return -ENOMEM;
- }
- }
-
- spin_lock_init(&d->lock);
-
- /* initialize tasklet */
- d->ctrlSet = context_base + OHCI1394_ContextControlSet;
- d->ctrlClear = context_base + OHCI1394_ContextControlClear;
- d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
- tasklet_init(&d->task, dma_trm_tasklet, (unsigned long)d);
- return 0;
-}
-
-static void ohci_set_hw_config_rom(struct hpsb_host *host, __be32 *config_rom)
-{
- struct ti_ohci *ohci = host->hostdata;
-
- reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(config_rom[0]));
- reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(config_rom[2]));
-
- memcpy(ohci->csr_config_rom_cpu, config_rom, OHCI_CONFIG_ROM_LEN);
-}
-
-
-static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
- quadlet_t data, quadlet_t compare)
-{
- struct ti_ohci *ohci = host->hostdata;
- int i;
-
- reg_write(ohci, OHCI1394_CSRData, data);
- reg_write(ohci, OHCI1394_CSRCompareData, compare);
- reg_write(ohci, OHCI1394_CSRControl, reg & 0x3);
-
- for (i = 0; i < OHCI_LOOP_COUNT; i++) {
- if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000)
- break;
-
- mdelay(1);
- }
-
- return reg_read(ohci, OHCI1394_CSRData);
-}
-
-static struct hpsb_host_driver ohci1394_driver = {
- .owner = THIS_MODULE,
- .name = OHCI1394_DRIVER_NAME,
- .set_hw_config_rom = ohci_set_hw_config_rom,
- .transmit_packet = ohci_transmit,
- .devctl = ohci_devctl,
- .isoctl = ohci_isoctl,
- .hw_csr_reg = ohci_hw_csr_reg,
-};
-
-/***********************************
- * PCI Driver Interface functions *
- ***********************************/
-
-#ifdef CONFIG_PPC_PMAC
-static void ohci1394_pmac_on(struct pci_dev *dev)
-{
- if (machine_is(powermac)) {
- struct device_node *ofn = pci_device_to_OF_node(dev);
-
- if (ofn) {
- pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 1);
- pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1);
- }
- }
-}
-
-static void ohci1394_pmac_off(struct pci_dev *dev)
-{
- if (machine_is(powermac)) {
- struct device_node *ofn = pci_device_to_OF_node(dev);
-
- if (ofn) {
- pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0);
- pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 0);
- }
- }
-}
-#else
-#define ohci1394_pmac_on(dev)
-#define ohci1394_pmac_off(dev)
-#endif /* CONFIG_PPC_PMAC */
-
-static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
-{
- struct hpsb_host *host;
- struct ti_ohci *ohci; /* shortcut to currently handled device */
- resource_size_t ohci_base;
- int err = -ENOMEM;
-
- ohci1394_pmac_on(dev);
- if (pci_enable_device(dev)) {
- PRINT_G(KERN_ERR, "Failed to enable OHCI hardware");
- err = -ENXIO;
- goto err;
- }
- pci_set_master(dev);
-
- host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev);
- if (!host) {
- PRINT_G(KERN_ERR, "Failed to allocate %s", "host structure");
- goto err;
- }
- ohci = host->hostdata;
- ohci->dev = dev;
- ohci->host = host;
- ohci->init_state = OHCI_INIT_ALLOC_HOST;
- host->pdev = dev;
- pci_set_drvdata(dev, ohci);
-
- /* We don't want hardware swapping */
- pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
-
- /* Some oddball Apple controllers do not order the selfid
- * properly, so we make up for it here. */
-#ifndef __LITTLE_ENDIAN
- /* XXX: Need a better way to check this. I'm wondering if we can
- * read the values of the OHCI1394_PCI_HCI_Control and the
- * noByteSwapData registers to see if they were not cleared to
- * zero. Should this work? Obviously it's not defined what these
- * registers will read when they aren't supported. Bleh! */
- if (dev->vendor == PCI_VENDOR_ID_APPLE &&
- dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
- ohci->no_swap_incoming = 1;
- ohci->selfid_swap = 0;
- } else
- ohci->selfid_swap = 1;
-#endif
-
-
-#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_FW
-#define PCI_DEVICE_ID_NVIDIA_NFORCE2_FW 0x006e
-#endif
-
- /* These chipsets require a bit of extra care when checking after
- * a busreset. */
- if ((dev->vendor == PCI_VENDOR_ID_APPLE &&
- dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) ||
- (dev->vendor == PCI_VENDOR_ID_NVIDIA &&
- dev->device == PCI_DEVICE_ID_NVIDIA_NFORCE2_FW))
- ohci->check_busreset = 1;
-
- /* We hardwire the MMIO length, since some CardBus adaptors
- * fail to report the right length. Anyway, the ohci spec
- * clearly says it's 2kb, so this shouldn't be a problem. */
- ohci_base = pci_resource_start(dev, 0);
- if (pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE)
- PRINT(KERN_WARNING, "PCI resource length of 0x%llx too small!",
- (unsigned long long)pci_resource_len(dev, 0));
-
- if (!request_mem_region(ohci_base, OHCI1394_REGISTER_SIZE,
- OHCI1394_DRIVER_NAME)) {
- PRINT_G(KERN_ERR, "MMIO resource (0x%llx - 0x%llx) unavailable",
- (unsigned long long)ohci_base,
- (unsigned long long)ohci_base + OHCI1394_REGISTER_SIZE);
- goto err;
- }
- ohci->init_state = OHCI_INIT_HAVE_MEM_REGION;
-
- ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE);
- if (ohci->registers == NULL) {
- PRINT_G(KERN_ERR, "Failed to remap registers");
- err = -ENXIO;
- goto err;
- }
- ohci->init_state = OHCI_INIT_HAVE_IOMAPPING;
- DBGMSG("Remapped memory spaces reg 0x%p", ohci->registers);
-
- /* csr_config rom allocation */
- ohci->csr_config_rom_cpu =
- pci_alloc_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN,
- &ohci->csr_config_rom_bus);
- if (ohci->csr_config_rom_cpu == NULL) {
- PRINT_G(KERN_ERR, "Failed to allocate %s", "buffer config rom");
- goto err;
- }
- ohci->init_state = OHCI_INIT_HAVE_CONFIG_ROM_BUFFER;
-
- /* self-id dma buffer allocation */
- ohci->selfid_buf_cpu =
- pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE,
- &ohci->selfid_buf_bus);
- if (ohci->selfid_buf_cpu == NULL) {
- PRINT_G(KERN_ERR, "Failed to allocate %s", "self-ID buffer");
- goto err;
- }
- ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER;
-
- if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff)
- PRINT(KERN_INFO, "SelfID buffer %p is not aligned on "
- "8Kb boundary... may cause problems on some CXD3222 chip",
- ohci->selfid_buf_cpu);
-
- /* No self-id errors at startup */
- ohci->self_id_errors = 0;
-
- ohci->init_state = OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE;
- /* AR DMA request context allocation */
- if (alloc_dma_rcv_ctx(ohci, &ohci->ar_req_context,
- DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
- AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
- OHCI1394_AsReqRcvContextBase) < 0) {
- PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Req context");
- goto err;
- }
- /* AR DMA response context allocation */
- if (alloc_dma_rcv_ctx(ohci, &ohci->ar_resp_context,
- DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
- AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
- OHCI1394_AsRspRcvContextBase) < 0) {
- PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Resp context");
- goto err;
- }
- /* AT DMA request context */
- if (alloc_dma_trm_ctx(ohci, &ohci->at_req_context,
- DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
- OHCI1394_AsReqTrContextBase) < 0) {
- PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Req context");
- goto err;
- }
- /* AT DMA response context */
- if (alloc_dma_trm_ctx(ohci, &ohci->at_resp_context,
- DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
- OHCI1394_AsRspTrContextBase) < 0) {
- PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Resp context");
- goto err;
- }
- /* Start off with a soft reset, to clear everything to a sane
- * state. */
- ohci_soft_reset(ohci);
-
- /* Now enable LPS, which we need in order to start accessing
- * most of the registers. In fact, on some cards (ALI M5251),
- * accessing registers in the SClk domain without LPS enabled
- * will lock up the machine. */
- reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
-
- /* Disable and clear interrupts */
- reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
-
- /* Flush MMIO writes and wait to make sure we have full link enabled. */
- reg_read(ohci, OHCI1394_Version);
- msleep(50);
-
- /* Determine the number of available IR and IT contexts. */
- ohci->nb_iso_rcv_ctx =
- get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
- ohci->nb_iso_xmit_ctx =
- get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
-
- /* Set the usage bits for non-existent contexts so they can't
- * be allocated */
- ohci->ir_ctx_usage = ~0 << ohci->nb_iso_rcv_ctx;
- ohci->it_ctx_usage = ~0 << ohci->nb_iso_xmit_ctx;
-
- INIT_LIST_HEAD(&ohci->iso_tasklet_list);
- spin_lock_init(&ohci->iso_tasklet_list_lock);
- ohci->ISO_channel_usage = 0;
- spin_lock_init(&ohci->IR_channel_lock);
-
- spin_lock_init(&ohci->event_lock);
-
- /*
- * interrupts are disabled, all right, but... due to IRQF_SHARED we
- * might get called anyway. We'll see no event, of course, but
- * we need to get to that "no event", so enough should be initialized
- * by that point.
- */
- err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED,
- OHCI1394_DRIVER_NAME, ohci);
- if (err) {
- PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq);
- goto err;
- }
- ohci->init_state = OHCI_INIT_HAVE_IRQ;
- ohci_initialize(ohci);
-
- /* Set certain csr values */
- host->csr.guid_hi = reg_read(ohci, OHCI1394_GUIDHi);
- host->csr.guid_lo = reg_read(ohci, OHCI1394_GUIDLo);
- host->csr.cyc_clk_acc = 100; /* how do we determine clk accuracy? */
- host->csr.max_rec = (reg_read(ohci, OHCI1394_BusOptions) >> 12) & 0xf;
- host->csr.lnk_spd = reg_read(ohci, OHCI1394_BusOptions) & 0x7;
-
- if (phys_dma) {
- host->low_addr_space =
- (u64) reg_read(ohci, OHCI1394_PhyUpperBound) << 16;
- if (!host->low_addr_space)
- host->low_addr_space = OHCI1394_PHYS_UPPER_BOUND_FIXED;
- }
- host->middle_addr_space = OHCI1394_MIDDLE_ADDRESS_SPACE;
-
- /* Tell the highlevel this host is ready */
- if (hpsb_add_host(host)) {
- PRINT_G(KERN_ERR, "Failed to register host with highlevel");
- goto err;
- }
- ohci->init_state = OHCI_INIT_DONE;
-
- return 0;
-err:
- ohci1394_pci_remove(dev);
- return err;
-}
-
-static void ohci1394_pci_remove(struct pci_dev *dev)
-{
- struct ti_ohci *ohci;
- struct device *device;
-
- ohci = pci_get_drvdata(dev);
- if (!ohci)
- goto out;
-
- device = get_device(&ohci->host->device);
-
- switch (ohci->init_state) {
- case OHCI_INIT_DONE:
- hpsb_remove_host(ohci->host);
-
- /* Clear out BUS Options */
- reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
- reg_write(ohci, OHCI1394_BusOptions,
- (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) |
- 0x00ff0000);
- memset(ohci->csr_config_rom_cpu, 0, OHCI_CONFIG_ROM_LEN);
-
- case OHCI_INIT_HAVE_IRQ:
- /* Clear interrupt registers */
- reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
-
- /* Disable IRM Contender */
- set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4));
-
- /* Clear link control register */
- reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
-
- /* Let all other nodes know to ignore us */
- ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
-
- /* Soft reset before we start - this disables
- * interrupts and clears linkEnable and LPS. */
- ohci_soft_reset(ohci);
- free_irq(dev->irq, ohci);
-
- case OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE:
- /* The ohci_soft_reset() stops all DMA contexts, so we
- * dont need to do this. */
- free_dma_rcv_ctx(&ohci->ar_req_context);
- free_dma_rcv_ctx(&ohci->ar_resp_context);
- free_dma_trm_ctx(&ohci->at_req_context);
- free_dma_trm_ctx(&ohci->at_resp_context);
-
- case OHCI_INIT_HAVE_SELFID_BUFFER:
- pci_free_consistent(dev, OHCI1394_SI_DMA_BUF_SIZE,
- ohci->selfid_buf_cpu,
- ohci->selfid_buf_bus);
-
- case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER:
- pci_free_consistent(dev, OHCI_CONFIG_ROM_LEN,
- ohci->csr_config_rom_cpu,
- ohci->csr_config_rom_bus);
-
- case OHCI_INIT_HAVE_IOMAPPING:
- iounmap(ohci->registers);
-
- case OHCI_INIT_HAVE_MEM_REGION:
- release_mem_region(pci_resource_start(dev, 0),
- OHCI1394_REGISTER_SIZE);
-
- case OHCI_INIT_ALLOC_HOST:
- pci_set_drvdata(dev, NULL);
- }
-
- if (device)
- put_device(device);
-out:
- ohci1394_pmac_off(dev);
-}
-
-#ifdef CONFIG_PM
-static int ohci1394_pci_suspend(struct pci_dev *dev, pm_message_t state)
-{
- int err;
- struct ti_ohci *ohci = pci_get_drvdata(dev);
-
- if (!ohci) {
- printk(KERN_ERR "%s: tried to suspend nonexisting host\n",
- OHCI1394_DRIVER_NAME);
- return -ENXIO;
- }
- DBGMSG("suspend called");
-
- /* Clear the async DMA contexts and stop using the controller */
- hpsb_bus_reset(ohci->host);
-
- /* See ohci1394_pci_remove() for comments on this sequence */
- reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
- reg_write(ohci, OHCI1394_BusOptions,
- (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) |
- 0x00ff0000);
- reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
- set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4));
- reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
- ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
- ohci_soft_reset(ohci);
-
- free_irq(dev->irq, ohci);
- err = pci_save_state(dev);
- if (err) {
- PRINT(KERN_ERR, "pci_save_state failed with %d", err);
- return err;
- }
- err = pci_set_power_state(dev, pci_choose_state(dev, state));
- if (err)
- DBGMSG("pci_set_power_state failed with %d", err);
- ohci1394_pmac_off(dev);
-
- return 0;
-}
-
-static int ohci1394_pci_resume(struct pci_dev *dev)
-{
- int err;
- struct ti_ohci *ohci = pci_get_drvdata(dev);
-
- if (!ohci) {
- printk(KERN_ERR "%s: tried to resume nonexisting host\n",
- OHCI1394_DRIVER_NAME);
- return -ENXIO;
- }
- DBGMSG("resume called");
-
- ohci1394_pmac_on(dev);
- pci_set_power_state(dev, PCI_D0);
- pci_restore_state(dev);
- err = pci_enable_device(dev);
- if (err) {
- PRINT(KERN_ERR, "pci_enable_device failed with %d", err);
- return err;
- }
-
- /* See ohci1394_pci_probe() for comments on this sequence */
- ohci_soft_reset(ohci);
- reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
- reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
- reg_read(ohci, OHCI1394_Version);
- msleep(50);
-
- err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED,
- OHCI1394_DRIVER_NAME, ohci);
- if (err) {
- PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq);
- return err;
- }
-
- ohci_initialize(ohci);
-
- hpsb_resume_host(ohci->host);
- return 0;
-}
-#endif /* CONFIG_PM */
-
-static struct pci_device_id ohci1394_pci_tbl[] = {
- {
- .class = PCI_CLASS_SERIAL_FIREWIRE_OHCI,
- .class_mask = PCI_ANY_ID,
- .vendor = PCI_ANY_ID,
- .device = PCI_ANY_ID,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
- { 0, },
-};
-
-MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);
-
-static struct pci_driver ohci1394_pci_driver = {
- .name = OHCI1394_DRIVER_NAME,
- .id_table = ohci1394_pci_tbl,
- .probe = ohci1394_pci_probe,
- .remove = ohci1394_pci_remove,
-#ifdef CONFIG_PM
- .resume = ohci1394_pci_resume,
- .suspend = ohci1394_pci_suspend,
-#endif
-};
-
-/***********************************
- * OHCI1394 Video Interface *
- ***********************************/
-
-/* essentially the only purpose of this code is to allow another
- module to hook into ohci's interrupt handler */
-
-/* returns zero if successful, one if DMA context is locked up */
-int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
-{
- int i=0;
-
- /* stop the channel program if it's still running */
- reg_write(ohci, reg, 0x8000);
-
- /* Wait until it effectively stops */
- while (reg_read(ohci, reg) & 0x400) {
- i++;
- if (i>5000) {
- PRINT(KERN_ERR,
- "Runaway loop while stopping context: %s...", msg ? msg : "");
- return 1;
- }
-
- mb();
- udelay(10);
- }
- if (msg) PRINT(KERN_ERR, "%s: dma prg stopped", msg);
- return 0;
-}
-
-void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type,
- void (*func)(unsigned long), unsigned long data)
-{
- tasklet_init(&tasklet->tasklet, func, data);
- tasklet->type = type;
- /* We init the tasklet->link field, so we can list_del() it
- * without worrying whether it was added to the list or not. */
- INIT_LIST_HEAD(&tasklet->link);
-}
-
-int ohci1394_register_iso_tasklet(struct ti_ohci *ohci,
- struct ohci1394_iso_tasklet *tasklet)
-{
- unsigned long flags, *usage;
- int n, i, r = -EBUSY;
-
- if (tasklet->type == OHCI_ISO_TRANSMIT) {
- n = ohci->nb_iso_xmit_ctx;
- usage = &ohci->it_ctx_usage;
- }
- else {
- n = ohci->nb_iso_rcv_ctx;
- usage = &ohci->ir_ctx_usage;
-
- /* only one receive context can be multichannel (OHCI sec 10.4.1) */
- if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
- if (test_and_set_bit(0, &ohci->ir_multichannel_used)) {
- return r;
- }
- }
- }
-
- spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
-
- for (i = 0; i < n; i++)
- if (!test_and_set_bit(i, usage)) {
- tasklet->context = i;
- list_add_tail(&tasklet->link, &ohci->iso_tasklet_list);
- r = 0;
- break;
- }
-
- spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
-
- return r;
-}
-
-void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
- struct ohci1394_iso_tasklet *tasklet)
-{
- unsigned long flags;
-
- tasklet_kill(&tasklet->tasklet);
-
- spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
-
- if (tasklet->type == OHCI_ISO_TRANSMIT)
- clear_bit(tasklet->context, &ohci->it_ctx_usage);
- else {
- clear_bit(tasklet->context, &ohci->ir_ctx_usage);
-
- if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
- clear_bit(0, &ohci->ir_multichannel_used);
- }
- }
-
- list_del(&tasklet->link);
-
- spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
-}
-
-EXPORT_SYMBOL(ohci1394_stop_context);
-EXPORT_SYMBOL(ohci1394_init_iso_tasklet);
-EXPORT_SYMBOL(ohci1394_register_iso_tasklet);
-EXPORT_SYMBOL(ohci1394_unregister_iso_tasklet);
-
-/***********************************
- * General module initialization *
- ***********************************/
-
-MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
-MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers");
-MODULE_LICENSE("GPL");
-
-static void __exit ohci1394_cleanup (void)
-{
- pci_unregister_driver(&ohci1394_pci_driver);
-}
-
-static int __init ohci1394_init(void)
-{
- return pci_register_driver(&ohci1394_pci_driver);
-}
-
-module_init(ohci1394_init);
-module_exit(ohci1394_cleanup);
diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h
deleted file mode 100644
index 7fb8ab9780ae..000000000000
--- a/drivers/ieee1394/ohci1394.h
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * ohci1394.h - driver for OHCI 1394 boards
- * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
- * Gord Peters <GordPeters@smarttech.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.
- */
-
-#ifndef _OHCI1394_H
-#define _OHCI1394_H
-
-#include "ieee1394_types.h"
-#include <asm/io.h>
-
-#define OHCI1394_DRIVER_NAME "ohci1394"
-
-#define OHCI1394_MAX_AT_REQ_RETRIES 0xf
-#define OHCI1394_MAX_AT_RESP_RETRIES 0x2
-#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
-#define OHCI1394_MAX_SELF_ID_ERRORS 16
-
-#define AR_REQ_NUM_DESC 4 /* number of AR req descriptors */
-#define AR_REQ_BUF_SIZE PAGE_SIZE /* size of AR req buffers */
-#define AR_REQ_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */
-
-#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */
-#define AR_RESP_BUF_SIZE PAGE_SIZE /* size of AR resp buffers */
-#define AR_RESP_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */
-
-#define IR_NUM_DESC 16 /* number of IR descriptors */
-#define IR_BUF_SIZE PAGE_SIZE /* 4096 bytes/buffer */
-#define IR_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */
-
-#define IT_NUM_DESC 16 /* number of IT descriptors */
-
-#define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */
-#define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */
-
-#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */
-
-#define OHCI_CONFIG_ROM_LEN 1024 /* Length of the mapped configrom space */
-
-#define OHCI1394_SI_DMA_BUF_SIZE 8192 /* length of the selfid buffer */
-
-/* PCI configuration space addresses */
-#define OHCI1394_PCI_HCI_Control 0x40
-
-struct dma_cmd {
- u32 control;
- u32 address;
- u32 branchAddress;
- u32 status;
-};
-
-/*
- * FIXME:
- * It is important that a single at_dma_prg does not cross a page boundary
- * The proper way to do it would be to do the check dynamically as the
- * programs are inserted into the AT fifo.
- */
-struct at_dma_prg {
- struct dma_cmd begin;
- quadlet_t data[4];
- struct dma_cmd end;
- quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */
-};
-
-/* identify whether a DMA context is asynchronous or isochronous */
-enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO };
-
-/* DMA receive context */
-struct dma_rcv_ctx {
- struct ti_ohci *ohci;
- enum context_type type;
- int ctx;
- unsigned int num_desc;
-
- unsigned int buf_size;
- unsigned int split_buf_size;
-
- /* dma block descriptors */
- struct dma_cmd **prg_cpu;
- dma_addr_t *prg_bus;
- struct pci_pool *prg_pool;
-
- /* dma buffers */
- quadlet_t **buf_cpu;
- dma_addr_t *buf_bus;
-
- unsigned int buf_ind;
- unsigned int buf_offset;
- quadlet_t *spb;
- spinlock_t lock;
- struct tasklet_struct task;
- int ctrlClear;
- int ctrlSet;
- int cmdPtr;
- int ctxtMatch;
-};
-
-/* DMA transmit context */
-struct dma_trm_ctx {
- struct ti_ohci *ohci;
- enum context_type type;
- int ctx;
- unsigned int num_desc;
-
- /* dma block descriptors */
- struct at_dma_prg **prg_cpu;
- dma_addr_t *prg_bus;
- struct pci_pool *prg_pool;
-
- unsigned int prg_ind;
- unsigned int sent_ind;
- int free_prgs;
- quadlet_t *branchAddrPtr;
-
- /* list of packets inserted in the AT FIFO */
- struct list_head fifo_list;
-
- /* list of pending packets to be inserted in the AT FIFO */
- struct list_head pending_list;
-
- spinlock_t lock;
- struct tasklet_struct task;
- int ctrlClear;
- int ctrlSet;
- int cmdPtr;
-};
-
-struct ohci1394_iso_tasklet {
- struct tasklet_struct tasklet;
- struct list_head link;
- int context;
- enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE,
- OHCI_ISO_MULTICHANNEL_RECEIVE } type;
-};
-
-struct ti_ohci {
- struct pci_dev *dev;
-
- enum {
- OHCI_INIT_ALLOC_HOST,
- OHCI_INIT_HAVE_MEM_REGION,
- OHCI_INIT_HAVE_IOMAPPING,
- OHCI_INIT_HAVE_CONFIG_ROM_BUFFER,
- OHCI_INIT_HAVE_SELFID_BUFFER,
- OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE,
- OHCI_INIT_HAVE_IRQ,
- OHCI_INIT_DONE,
- } init_state;
-
- /* remapped memory spaces */
- void __iomem *registers;
-
- /* dma buffer for self-id packets */
- quadlet_t *selfid_buf_cpu;
- dma_addr_t selfid_buf_bus;
-
- /* buffer for csr config rom */
- quadlet_t *csr_config_rom_cpu;
- dma_addr_t csr_config_rom_bus;
- int csr_config_rom_length;
-
- unsigned int max_packet_size;
-
- /* async receive */
- struct dma_rcv_ctx ar_resp_context;
- struct dma_rcv_ctx ar_req_context;
-
- /* async transmit */
- struct dma_trm_ctx at_resp_context;
- struct dma_trm_ctx at_req_context;
-
- /* iso receive */
- int nb_iso_rcv_ctx;
- unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */
- unsigned long ir_multichannel_used; /* ditto */
- spinlock_t IR_channel_lock;
-
- /* iso transmit */
- int nb_iso_xmit_ctx;
- unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */
-
- u64 ISO_channel_usage;
-
- /* IEEE-1394 part follows */
- struct hpsb_host *host;
-
- int phyid, isroot;
-
- spinlock_t phy_reg_lock;
- spinlock_t event_lock;
-
- int self_id_errors;
-
- /* Tasklets for iso receive and transmit, used by video1394
- * and dv1394 */
- struct list_head iso_tasklet_list;
- spinlock_t iso_tasklet_list_lock;
-
- /* Swap the selfid buffer? */
- unsigned int selfid_swap:1;
- /* Some Apple chipset seem to swap incoming headers for us */
- unsigned int no_swap_incoming:1;
-
- /* Force extra paranoia checking on bus-reset handling */
- unsigned int check_busreset:1;
-};
-
-static inline int cross_bound(unsigned long addr, unsigned int size)
-{
- if (size == 0)
- return 0;
-
- if (size > PAGE_SIZE)
- return 1;
-
- if (addr >> PAGE_SHIFT != (addr + size - 1) >> PAGE_SHIFT)
- return 1;
-
- return 0;
-}
-
-/*
- * Register read and write helper functions.
- */
-static inline void reg_write(const struct ti_ohci *ohci, int offset, u32 data)
-{
- writel(data, ohci->registers + offset);
-}
-
-static inline u32 reg_read(const struct ti_ohci *ohci, int offset)
-{
- return readl(ohci->registers + offset);
-}
-
-
-/* 2 KiloBytes of register space */
-#define OHCI1394_REGISTER_SIZE 0x800
-
-/* Offsets relative to context bases defined below */
-
-#define OHCI1394_ContextControlSet 0x000
-#define OHCI1394_ContextControlClear 0x004
-#define OHCI1394_ContextCommandPtr 0x00C
-
-/* register map */
-#define OHCI1394_Version 0x000
-#define OHCI1394_GUID_ROM 0x004
-#define OHCI1394_ATRetries 0x008
-#define OHCI1394_CSRData 0x00C
-#define OHCI1394_CSRCompareData 0x010
-#define OHCI1394_CSRControl 0x014
-#define OHCI1394_ConfigROMhdr 0x018
-#define OHCI1394_BusID 0x01C
-#define OHCI1394_BusOptions 0x020
-#define OHCI1394_GUIDHi 0x024
-#define OHCI1394_GUIDLo 0x028
-#define OHCI1394_ConfigROMmap 0x034
-#define OHCI1394_PostedWriteAddressLo 0x038
-#define OHCI1394_PostedWriteAddressHi 0x03C
-#define OHCI1394_VendorID 0x040
-#define OHCI1394_HCControlSet 0x050
-#define OHCI1394_HCControlClear 0x054
-#define OHCI1394_HCControl_noByteSwap 0x40000000
-#define OHCI1394_HCControl_programPhyEnable 0x00800000
-#define OHCI1394_HCControl_aPhyEnhanceEnable 0x00400000
-#define OHCI1394_HCControl_LPS 0x00080000
-#define OHCI1394_HCControl_postedWriteEnable 0x00040000
-#define OHCI1394_HCControl_linkEnable 0x00020000
-#define OHCI1394_HCControl_softReset 0x00010000
-#define OHCI1394_SelfIDBuffer 0x064
-#define OHCI1394_SelfIDCount 0x068
-#define OHCI1394_IRMultiChanMaskHiSet 0x070
-#define OHCI1394_IRMultiChanMaskHiClear 0x074
-#define OHCI1394_IRMultiChanMaskLoSet 0x078
-#define OHCI1394_IRMultiChanMaskLoClear 0x07C
-#define OHCI1394_IntEventSet 0x080
-#define OHCI1394_IntEventClear 0x084
-#define OHCI1394_IntMaskSet 0x088
-#define OHCI1394_IntMaskClear 0x08C
-#define OHCI1394_IsoXmitIntEventSet 0x090
-#define OHCI1394_IsoXmitIntEventClear 0x094
-#define OHCI1394_IsoXmitIntMaskSet 0x098
-#define OHCI1394_IsoXmitIntMaskClear 0x09C
-#define OHCI1394_IsoRecvIntEventSet 0x0A0
-#define OHCI1394_IsoRecvIntEventClear 0x0A4
-#define OHCI1394_IsoRecvIntMaskSet 0x0A8
-#define OHCI1394_IsoRecvIntMaskClear 0x0AC
-#define OHCI1394_InitialBandwidthAvailable 0x0B0
-#define OHCI1394_InitialChannelsAvailableHi 0x0B4
-#define OHCI1394_InitialChannelsAvailableLo 0x0B8
-#define OHCI1394_FairnessControl 0x0DC
-#define OHCI1394_LinkControlSet 0x0E0
-#define OHCI1394_LinkControlClear 0x0E4
-#define OHCI1394_LinkControl_RcvSelfID 0x00000200
-#define OHCI1394_LinkControl_RcvPhyPkt 0x00000400
-#define OHCI1394_LinkControl_CycleTimerEnable 0x00100000
-#define OHCI1394_LinkControl_CycleMaster 0x00200000
-#define OHCI1394_LinkControl_CycleSource 0x00400000
-#define OHCI1394_NodeID 0x0E8
-#define OHCI1394_PhyControl 0x0EC
-#define OHCI1394_IsochronousCycleTimer 0x0F0
-#define OHCI1394_AsReqFilterHiSet 0x100
-#define OHCI1394_AsReqFilterHiClear 0x104
-#define OHCI1394_AsReqFilterLoSet 0x108
-#define OHCI1394_AsReqFilterLoClear 0x10C
-#define OHCI1394_PhyReqFilterHiSet 0x110
-#define OHCI1394_PhyReqFilterHiClear 0x114
-#define OHCI1394_PhyReqFilterLoSet 0x118
-#define OHCI1394_PhyReqFilterLoClear 0x11C
-#define OHCI1394_PhyUpperBound 0x120
-
-#define OHCI1394_AsReqTrContextBase 0x180
-#define OHCI1394_AsReqTrContextControlSet 0x180
-#define OHCI1394_AsReqTrContextControlClear 0x184
-#define OHCI1394_AsReqTrCommandPtr 0x18C
-
-#define OHCI1394_AsRspTrContextBase 0x1A0
-#define OHCI1394_AsRspTrContextControlSet 0x1A0
-#define OHCI1394_AsRspTrContextControlClear 0x1A4
-#define OHCI1394_AsRspTrCommandPtr 0x1AC
-
-#define OHCI1394_AsReqRcvContextBase 0x1C0
-#define OHCI1394_AsReqRcvContextControlSet 0x1C0
-#define OHCI1394_AsReqRcvContextControlClear 0x1C4
-#define OHCI1394_AsReqRcvCommandPtr 0x1CC
-
-#define OHCI1394_AsRspRcvContextBase 0x1E0
-#define OHCI1394_AsRspRcvContextControlSet 0x1E0
-#define OHCI1394_AsRspRcvContextControlClear 0x1E4
-#define OHCI1394_AsRspRcvCommandPtr 0x1EC
-
-/* Isochronous transmit registers */
-/* Add (16 * n) for context n */
-#define OHCI1394_IsoXmitContextBase 0x200
-#define OHCI1394_IsoXmitContextControlSet 0x200
-#define OHCI1394_IsoXmitContextControlClear 0x204
-#define OHCI1394_IsoXmitCommandPtr 0x20C
-
-/* Isochronous receive registers */
-/* Add (32 * n) for context n */
-#define OHCI1394_IsoRcvContextBase 0x400
-#define OHCI1394_IsoRcvContextControlSet 0x400
-#define OHCI1394_IsoRcvContextControlClear 0x404
-#define OHCI1394_IsoRcvCommandPtr 0x40C
-#define OHCI1394_IsoRcvContextMatch 0x410
-
-/* Interrupts Mask/Events */
-
-#define OHCI1394_reqTxComplete 0x00000001
-#define OHCI1394_respTxComplete 0x00000002
-#define OHCI1394_ARRQ 0x00000004
-#define OHCI1394_ARRS 0x00000008
-#define OHCI1394_RQPkt 0x00000010
-#define OHCI1394_RSPkt 0x00000020
-#define OHCI1394_isochTx 0x00000040
-#define OHCI1394_isochRx 0x00000080
-#define OHCI1394_postedWriteErr 0x00000100
-#define OHCI1394_lockRespErr 0x00000200
-#define OHCI1394_selfIDComplete 0x00010000
-#define OHCI1394_busReset 0x00020000
-#define OHCI1394_phy 0x00080000
-#define OHCI1394_cycleSynch 0x00100000
-#define OHCI1394_cycle64Seconds 0x00200000
-#define OHCI1394_cycleLost 0x00400000
-#define OHCI1394_cycleInconsistent 0x00800000
-#define OHCI1394_unrecoverableError 0x01000000
-#define OHCI1394_cycleTooLong 0x02000000
-#define OHCI1394_phyRegRcvd 0x04000000
-#define OHCI1394_masterIntEnable 0x80000000
-
-/* DMA Control flags */
-#define DMA_CTL_OUTPUT_MORE 0x00000000
-#define DMA_CTL_OUTPUT_LAST 0x10000000
-#define DMA_CTL_INPUT_MORE 0x20000000
-#define DMA_CTL_INPUT_LAST 0x30000000
-#define DMA_CTL_UPDATE 0x08000000
-#define DMA_CTL_IMMEDIATE 0x02000000
-#define DMA_CTL_IRQ 0x00300000
-#define DMA_CTL_BRANCH 0x000c0000
-#define DMA_CTL_WAIT 0x00030000
-
-/* OHCI evt_* error types, table 3-2 of the OHCI 1.1 spec. */
-#define EVT_NO_STATUS 0x0 /* No event status */
-#define EVT_RESERVED_A 0x1 /* Reserved, not used !!! */
-#define EVT_LONG_PACKET 0x2 /* The revc data was longer than the buf */
-#define EVT_MISSING_ACK 0x3 /* A subaction gap was detected before an ack
- arrived, or recv'd ack had a parity error */
-#define EVT_UNDERRUN 0x4 /* Underrun on corresponding FIFO, packet
- truncated */
-#define EVT_OVERRUN 0x5 /* A recv FIFO overflowed on reception of ISO
- packet */
-#define EVT_DESCRIPTOR_READ 0x6 /* An unrecoverable error occurred while host was
- reading a descriptor block */
-#define EVT_DATA_READ 0x7 /* An error occurred while host controller was
- attempting to read from host memory in the data
- stage of descriptor processing */
-#define EVT_DATA_WRITE 0x8 /* An error occurred while host controller was
- attempting to write either during the data stage
- of descriptor processing, or when processing a single
- 16-bit host memory write */
-#define EVT_BUS_RESET 0x9 /* Identifies a PHY packet in the recv buffer as
- being a synthesized bus reset packet */
-#define EVT_TIMEOUT 0xa /* Indicates that the asynchronous transmit response
- packet expired and was not transmitted, or that an
- IT DMA context experienced a skip processing overflow */
-#define EVT_TCODE_ERR 0xb /* A bad tCode is associated with this packet.
- The packet was flushed */
-#define EVT_RESERVED_B 0xc /* Reserved, not used !!! */
-#define EVT_RESERVED_C 0xd /* Reserved, not used !!! */
-#define EVT_UNKNOWN 0xe /* An error condition has occurred that cannot be
- represented by any other event codes defined herein. */
-#define EVT_FLUSHED 0xf /* Send by the link side of output FIFO when asynchronous
- packets are being flushed due to a bus reset. */
-
-#define OHCI1394_TCODE_PHY 0xE
-
-/* Node offset map (phys DMA area, posted write area).
- * The value of OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED may be modified but must
- * be lower than OHCI1394_MIDDLE_ADDRESS_SPACE.
- * OHCI1394_PHYS_UPPER_BOUND_FIXED and OHCI1394_MIDDLE_ADDRESS_SPACE are
- * constants given by the OHCI spec.
- */
-#define OHCI1394_PHYS_UPPER_BOUND_FIXED 0x000100000000ULL /* 4 GB */
-#define OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED 0x010000000000ULL /* 1 TB */
-#define OHCI1394_MIDDLE_ADDRESS_SPACE 0xffff00000000ULL
-
-void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet,
- int type,
- void (*func)(unsigned long),
- unsigned long data);
-int ohci1394_register_iso_tasklet(struct ti_ohci *ohci,
- struct ohci1394_iso_tasklet *tasklet);
-void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
- struct ohci1394_iso_tasklet *tasklet);
-int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg);
-struct ti_ohci *ohci1394_get_struct(int card_num);
-
-#endif
diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c
deleted file mode 100644
index bf47fee79808..000000000000
--- a/drivers/ieee1394/pcilynx.c
+++ /dev/null
@@ -1,1554 +0,0 @@
-/*
- * pcilynx.c - Texas Instruments PCILynx driver
- * Copyright (C) 1999,2000 Andreas Bombe <andreas.bombe@munich.netsurf.de>,
- * Stephan Linz <linz@mazet.de>
- * Manfred Weihs <weihs@ict.tuwien.ac.at>
- *
- * 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.
- */
-
-/*
- * Contributions:
- *
- * Manfred Weihs <weihs@ict.tuwien.ac.at>
- * reading bus info block (containing GUID) from serial
- * eeprom via i2c and storing it in config ROM
- * Reworked code for initiating bus resets
- * (long, short, with or without hold-off)
- * Enhancements in async and iso send code
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/kdev_t.h>
-#include <linux/dma-mapping.h>
-#include <asm/byteorder.h>
-#include <asm/atomic.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-
-#include "csr1212.h"
-#include "ieee1394.h"
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "pcilynx.h"
-
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-/* print general (card independent) information */
-#define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)
-/* print card specific information */
-#define PRINT(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)
-
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-#define PRINT_GD(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)
-#define PRINTD(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)
-#else
-#define PRINT_GD(level, fmt, args...) do {} while (0)
-#define PRINTD(level, card, fmt, args...) do {} while (0)
-#endif
-
-
-/* Module Parameters */
-static int skip_eeprom;
-module_param(skip_eeprom, int, 0444);
-MODULE_PARM_DESC(skip_eeprom, "Use generic bus info block instead of serial eeprom (default = 0).");
-
-
-static struct hpsb_host_driver lynx_driver;
-static unsigned int card_id;
-
-
-
-/*
- * I2C stuff
- */
-
-/* the i2c stuff was inspired by i2c-philips-par.c */
-
-static void bit_setscl(void *data, int state)
-{
- if (state) {
- ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000040;
- } else {
- ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000040;
- }
- reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state);
-}
-
-static void bit_setsda(void *data, int state)
-{
- if (state) {
- ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000010;
- } else {
- ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000010;
- }
- reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state);
-}
-
-static int bit_getscl(void *data)
-{
- return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000040;
-}
-
-static int bit_getsda(void *data)
-{
- return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000010;
-}
-
-static struct i2c_algo_bit_data bit_data = {
- .setsda = bit_setsda,
- .setscl = bit_setscl,
- .getsda = bit_getsda,
- .getscl = bit_getscl,
- .udelay = 5,
- .timeout = 100,
-};
-
-
-/*
- * PCL handling functions.
- */
-
-static pcl_t alloc_pcl(struct ti_lynx *lynx)
-{
- u8 m;
- int i, j;
-
- spin_lock(&lynx->lock);
- /* FIXME - use ffz() to make this readable */
- for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) {
- m = lynx->pcl_bmap[i];
- for (j = 0; j < 8; j++) {
- if (m & 1<<j) {
- continue;
- }
- m |= 1<<j;
- lynx->pcl_bmap[i] = m;
- spin_unlock(&lynx->lock);
- return 8 * i + j;
- }
- }
- spin_unlock(&lynx->lock);
-
- return -1;
-}
-
-
-#if 0
-static void free_pcl(struct ti_lynx *lynx, pcl_t pclid)
-{
- int off, bit;
-
- off = pclid / 8;
- bit = pclid % 8;
-
- if (pclid < 0) {
- return;
- }
-
- spin_lock(&lynx->lock);
- if (lynx->pcl_bmap[off] & 1<<bit) {
- lynx->pcl_bmap[off] &= ~(1<<bit);
- } else {
- PRINT(KERN_ERR, lynx->id,
- "attempted to free unallocated PCL %d", pclid);
- }
- spin_unlock(&lynx->lock);
-}
-
-/* functions useful for debugging */
-static void pretty_print_pcl(const struct ti_pcl *pcl)
-{
- int i;
-
- printk("PCL next %08x, userdata %08x, status %08x, remtrans %08x, nextbuf %08x\n",
- pcl->next, pcl->user_data, pcl->pcl_status,
- pcl->remaining_transfer_count, pcl->next_data_buffer);
-
- printk("PCL");
- for (i=0; i<13; i++) {
- printk(" c%x:%08x d%x:%08x",
- i, pcl->buffer[i].control, i, pcl->buffer[i].pointer);
- if (!(i & 0x3) && (i != 12)) printk("\nPCL");
- }
- printk("\n");
-}
-
-static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid)
-{
- struct ti_pcl pcl;
-
- get_pcl(lynx, pclid, &pcl);
- pretty_print_pcl(&pcl);
-}
-#endif
-
-
-
-/***********************************
- * IEEE-1394 functionality section *
- ***********************************/
-
-
-static int get_phy_reg(struct ti_lynx *lynx, int addr)
-{
- int retval;
- int i = 0;
-
- unsigned long flags;
-
- if (addr > 15) {
- PRINT(KERN_ERR, lynx->id,
- "%s: PHY register address %d out of range",
- __func__, addr);
- return -1;
- }
-
- spin_lock_irqsave(&lynx->phy_reg_lock, flags);
-
- reg_write(lynx, LINK_PHY, LINK_PHY_READ | LINK_PHY_ADDR(addr));
- do {
- retval = reg_read(lynx, LINK_PHY);
-
- if (i > 10000) {
- PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting",
- __func__);
- retval = -1;
- break;
- }
- i++;
- } while ((retval & 0xf00) != LINK_PHY_RADDR(addr));
-
- reg_write(lynx, LINK_INT_STATUS, LINK_INT_PHY_REG_RCVD);
- spin_unlock_irqrestore(&lynx->phy_reg_lock, flags);
-
- if (retval != -1) {
- return retval & 0xff;
- } else {
- return -1;
- }
-}
-
-static int set_phy_reg(struct ti_lynx *lynx, int addr, int val)
-{
- unsigned long flags;
-
- if (addr > 15) {
- PRINT(KERN_ERR, lynx->id,
- "%s: PHY register address %d out of range", __func__, addr);
- return -1;
- }
-
- if (val > 0xff) {
- PRINT(KERN_ERR, lynx->id,
- "%s: PHY register value %d out of range", __func__, val);
- return -1;
- }
-
- spin_lock_irqsave(&lynx->phy_reg_lock, flags);
-
- reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr)
- | LINK_PHY_WDATA(val));
-
- spin_unlock_irqrestore(&lynx->phy_reg_lock, flags);
-
- return 0;
-}
-
-static int sel_phy_reg_page(struct ti_lynx *lynx, int page)
-{
- int reg;
-
- if (page > 7) {
- PRINT(KERN_ERR, lynx->id,
- "%s: PHY page %d out of range", __func__, page);
- return -1;
- }
-
- reg = get_phy_reg(lynx, 7);
- if (reg != -1) {
- reg &= 0x1f;
- reg |= (page << 5);
- set_phy_reg(lynx, 7, reg);
- return 0;
- } else {
- return -1;
- }
-}
-
-#if 0 /* not needed at this time */
-static int sel_phy_reg_port(struct ti_lynx *lynx, int port)
-{
- int reg;
-
- if (port > 15) {
- PRINT(KERN_ERR, lynx->id,
- "%s: PHY port %d out of range", __func__, port);
- return -1;
- }
-
- reg = get_phy_reg(lynx, 7);
- if (reg != -1) {
- reg &= 0xf0;
- reg |= port;
- set_phy_reg(lynx, 7, reg);
- return 0;
- } else {
- return -1;
- }
-}
-#endif
-
-static u32 get_phy_vendorid(struct ti_lynx *lynx)
-{
- u32 pvid = 0;
- sel_phy_reg_page(lynx, 1);
- pvid |= (get_phy_reg(lynx, 10) << 16);
- pvid |= (get_phy_reg(lynx, 11) << 8);
- pvid |= get_phy_reg(lynx, 12);
- PRINT(KERN_INFO, lynx->id, "PHY vendor id 0x%06x", pvid);
- return pvid;
-}
-
-static u32 get_phy_productid(struct ti_lynx *lynx)
-{
- u32 id = 0;
- sel_phy_reg_page(lynx, 1);
- id |= (get_phy_reg(lynx, 13) << 16);
- id |= (get_phy_reg(lynx, 14) << 8);
- id |= get_phy_reg(lynx, 15);
- PRINT(KERN_INFO, lynx->id, "PHY product id 0x%06x", id);
- return id;
-}
-
-static quadlet_t generate_own_selfid(struct ti_lynx *lynx,
- struct hpsb_host *host)
-{
- quadlet_t lsid;
- char phyreg[7];
- int i;
-
- phyreg[0] = lynx->phy_reg0;
- for (i = 1; i < 7; i++) {
- phyreg[i] = get_phy_reg(lynx, i);
- }
-
- /* FIXME? We assume a TSB21LV03A phy here. This code doesn't support
- more than 3 ports on the PHY anyway. */
-
- lsid = 0x80400000 | ((phyreg[0] & 0xfc) << 22);
- lsid |= (phyreg[1] & 0x3f) << 16; /* gap count */
- lsid |= (phyreg[2] & 0xc0) << 8; /* max speed */
- if (!hpsb_disable_irm)
- lsid |= (phyreg[6] & 0x01) << 11; /* contender (phy dependent) */
- /* lsid |= 1 << 11; *//* set contender (hack) */
- lsid |= (phyreg[6] & 0x10) >> 3; /* initiated reset */
-
- for (i = 0; i < (phyreg[2] & 0xf); i++) { /* ports */
- if (phyreg[3 + i] & 0x4) {
- lsid |= (((phyreg[3 + i] & 0x8) | 0x10) >> 3)
- << (6 - i*2);
- } else {
- lsid |= 1 << (6 - i*2);
- }
- }
-
- cpu_to_be32s(&lsid);
- PRINT(KERN_DEBUG, lynx->id, "generated own selfid 0x%x", lsid);
- return lsid;
-}
-
-static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host)
-{
- quadlet_t *q = lynx->rcv_page;
- int phyid, isroot, size;
- quadlet_t lsid = 0;
- int i;
-
- if (lynx->phy_reg0 == -1 || lynx->selfid_size == -1) return;
-
- size = lynx->selfid_size;
- phyid = lynx->phy_reg0;
-
- i = (size > 16 ? 16 : size) / 4 - 1;
- while (i >= 0) {
- cpu_to_be32s(&q[i]);
- i--;
- }
-
- if (!lynx->phyic.reg_1394a) {
- lsid = generate_own_selfid(lynx, host);
- }
-
- isroot = (phyid & 2) != 0;
- phyid >>= 2;
- PRINT(KERN_INFO, lynx->id, "SelfID process finished (phyid %d, %s)",
- phyid, (isroot ? "root" : "not root"));
- reg_write(lynx, LINK_ID, (0xffc0 | phyid) << 16);
-
- if (!lynx->phyic.reg_1394a && !size) {
- hpsb_selfid_received(host, lsid);
- }
-
- while (size > 0) {
- struct selfid *sid = (struct selfid *)q;
-
- if (!lynx->phyic.reg_1394a && !sid->extended
- && (sid->phy_id == (phyid + 1))) {
- hpsb_selfid_received(host, lsid);
- }
-
- if (q[0] == ~q[1]) {
- PRINT(KERN_DEBUG, lynx->id, "SelfID packet 0x%x rcvd",
- q[0]);
- hpsb_selfid_received(host, q[0]);
- } else {
- PRINT(KERN_INFO, lynx->id,
- "inconsistent selfid 0x%x/0x%x", q[0], q[1]);
- }
- q += 2;
- size -= 8;
- }
-
- if (!lynx->phyic.reg_1394a && isroot && phyid != 0) {
- hpsb_selfid_received(host, lsid);
- }
-
- hpsb_selfid_complete(host, phyid, isroot);
-
- if (host->in_bus_reset) return; /* in bus reset again */
-
- if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); //FIXME: I do not think, we need this here
- reg_set_bits(lynx, LINK_CONTROL,
- LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN
- | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN);
-}
-
-
-
-/* This must be called with the respective queue_lock held. */
-static void send_next(struct ti_lynx *lynx, int what)
-{
- struct ti_pcl pcl;
- struct lynx_send_data *d;
- struct hpsb_packet *packet;
-
-#if 0 /* has been removed from ieee1394 core */
- d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async);
-#else
- d = &lynx->async;
-#endif
- if (!list_empty(&d->pcl_queue)) {
- PRINT(KERN_ERR, lynx->id, "trying to queue a new packet in nonempty fifo");
- BUG();
- }
-
- packet = driver_packet(d->queue.next);
- list_move_tail(&packet->driver_list, &d->pcl_queue);
-
- d->header_dma = pci_map_single(lynx->dev, packet->header,
- packet->header_size, PCI_DMA_TODEVICE);
- if (packet->data_size) {
- d->data_dma = pci_map_single(lynx->dev, packet->data,
- packet->data_size,
- PCI_DMA_TODEVICE);
- } else {
- d->data_dma = 0;
- }
-
- pcl.next = PCL_NEXT_INVALID;
- pcl.async_error_next = PCL_NEXT_INVALID;
- pcl.pcl_status = 0;
- pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size;
-#ifndef __BIG_ENDIAN
- pcl.buffer[0].control |= PCL_BIGENDIAN;
-#endif
- pcl.buffer[0].pointer = d->header_dma;
- pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size;
- pcl.buffer[1].pointer = d->data_dma;
-
- switch (packet->type) {
- case hpsb_async:
- pcl.buffer[0].control |= PCL_CMD_XMT;
- break;
-#if 0 /* has been removed from ieee1394 core */
- case hpsb_iso:
- pcl.buffer[0].control |= PCL_CMD_XMT | PCL_ISOMODE;
- break;
-#endif
- case hpsb_raw:
- pcl.buffer[0].control |= PCL_CMD_UNFXMT;
- break;
- }
-
- put_pcl(lynx, d->pcl, &pcl);
- run_pcl(lynx, d->pcl_start, d->channel);
-}
-
-
-/* called from subsystem core */
-static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
-{
- struct ti_lynx *lynx = host->hostdata;
- struct lynx_send_data *d;
- unsigned long flags;
-
- if (packet->data_size >= 4096) {
- PRINT(KERN_ERR, lynx->id, "transmit packet data too big (%Zd)",
- packet->data_size);
- return -EOVERFLOW;
- }
-
- switch (packet->type) {
- case hpsb_async:
- case hpsb_raw:
- d = &lynx->async;
- break;
-#if 0 /* has been removed from ieee1394 core */
- case hpsb_iso:
- d = &lynx->iso_send;
- break;
-#endif
- default:
- PRINT(KERN_ERR, lynx->id, "invalid packet type %d",
- packet->type);
- return -EINVAL;
- }
-
- if (packet->tcode == TCODE_WRITEQ
- || packet->tcode == TCODE_READQ_RESPONSE) {
- cpu_to_be32s(&packet->header[3]);
- }
-
- spin_lock_irqsave(&d->queue_lock, flags);
-
- list_add_tail(&packet->driver_list, &d->queue);
- if (list_empty(&d->pcl_queue))
- send_next(lynx, packet->type);
-
- spin_unlock_irqrestore(&d->queue_lock, flags);
-
- return 0;
-}
-
-
-/* called from subsystem core */
-static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
-{
- struct ti_lynx *lynx = host->hostdata;
- int retval = 0;
- struct hpsb_packet *packet;
- LIST_HEAD(packet_list);
- unsigned long flags;
- int phy_reg;
-
- switch (cmd) {
- case RESET_BUS:
- if (reg_read(lynx, LINK_INT_STATUS) & LINK_INT_PHY_BUSRESET) {
- retval = 0;
- break;
- }
-
- switch (arg) {
- case SHORT_RESET:
- if (lynx->phyic.reg_1394a) {
- phy_reg = get_phy_reg(lynx, 5);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- phy_reg |= 0x40;
-
- PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset) on request");
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
- set_phy_reg(lynx, 5, phy_reg); /* set ISBR */
- break;
- } else {
- PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy");
- /* fall through to long bus reset */
- }
- case LONG_RESET:
- phy_reg = get_phy_reg(lynx, 1);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- phy_reg |= 0x40;
-
- PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset) on request");
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
- set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */
- break;
- case SHORT_RESET_NO_FORCE_ROOT:
- if (lynx->phyic.reg_1394a) {
- phy_reg = get_phy_reg(lynx, 1);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- if (phy_reg & 0x80) {
- phy_reg &= ~0x80;
- set_phy_reg(lynx, 1, phy_reg); /* clear RHB */
- }
-
- phy_reg = get_phy_reg(lynx, 5);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- phy_reg |= 0x40;
-
- PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, no force_root) on request");
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
- set_phy_reg(lynx, 5, phy_reg); /* set ISBR */
- break;
- } else {
- PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy");
- /* fall through to long bus reset */
- }
- case LONG_RESET_NO_FORCE_ROOT:
- phy_reg = get_phy_reg(lynx, 1);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- phy_reg &= ~0x80;
- phy_reg |= 0x40;
-
- PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, no force_root) on request");
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
- set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */
- break;
- case SHORT_RESET_FORCE_ROOT:
- if (lynx->phyic.reg_1394a) {
- phy_reg = get_phy_reg(lynx, 1);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- if (!(phy_reg & 0x80)) {
- phy_reg |= 0x80;
- set_phy_reg(lynx, 1, phy_reg); /* set RHB */
- }
-
- phy_reg = get_phy_reg(lynx, 5);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- phy_reg |= 0x40;
-
- PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, force_root set) on request");
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
- set_phy_reg(lynx, 5, phy_reg); /* set ISBR */
- break;
- } else {
- PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy");
- /* fall through to long bus reset */
- }
- case LONG_RESET_FORCE_ROOT:
- phy_reg = get_phy_reg(lynx, 1);
- if (phy_reg == -1) {
- PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
- retval = -1;
- break;
- }
- phy_reg |= 0xc0;
-
- PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, force_root set) on request");
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
- set_phy_reg(lynx, 1, phy_reg); /* set IBR and RHB */
- break;
- default:
- PRINT(KERN_ERR, lynx->id, "unknown argument for reset_bus command %d", arg);
- retval = -1;
- }
-
- break;
-
- case GET_CYCLE_COUNTER:
- retval = reg_read(lynx, CYCLE_TIMER);
- break;
-
- case SET_CYCLE_COUNTER:
- reg_write(lynx, CYCLE_TIMER, arg);
- break;
-
- case SET_BUS_ID:
- reg_write(lynx, LINK_ID,
- (arg << 22) | (reg_read(lynx, LINK_ID) & 0x003f0000));
- break;
-
- case ACT_CYCLE_MASTER:
- if (arg) {
- reg_set_bits(lynx, LINK_CONTROL,
- LINK_CONTROL_CYCMASTER);
- } else {
- reg_clear_bits(lynx, LINK_CONTROL,
- LINK_CONTROL_CYCMASTER);
- }
- break;
-
- case CANCEL_REQUESTS:
- spin_lock_irqsave(&lynx->async.queue_lock, flags);
-
- reg_write(lynx, DMA_CHAN_CTRL(CHANNEL_ASYNC_SEND), 0);
- list_splice_init(&lynx->async.queue, &packet_list);
-
- if (list_empty(&lynx->async.pcl_queue)) {
- spin_unlock_irqrestore(&lynx->async.queue_lock, flags);
- PRINTD(KERN_DEBUG, lynx->id, "no async packet in PCL to cancel");
- } else {
- struct ti_pcl pcl;
- u32 ack;
-
- PRINT(KERN_INFO, lynx->id, "cancelling async packet, that was already in PCL");
-
- get_pcl(lynx, lynx->async.pcl, &pcl);
-
- packet = driver_packet(lynx->async.pcl_queue.next);
- list_del_init(&packet->driver_list);
-
- pci_unmap_single(lynx->dev, lynx->async.header_dma,
- packet->header_size, PCI_DMA_TODEVICE);
- if (packet->data_size) {
- pci_unmap_single(lynx->dev, lynx->async.data_dma,
- packet->data_size, PCI_DMA_TODEVICE);
- }
-
- spin_unlock_irqrestore(&lynx->async.queue_lock, flags);
-
- if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
- if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
- ack = (pcl.pcl_status >> 15) & 0xf;
- PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
- ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
- } else {
- ack = (pcl.pcl_status >> 15) & 0xf;
- }
- } else {
- PRINT(KERN_INFO, lynx->id, "async packet was not completed");
- ack = ACKX_ABORTED;
- }
- hpsb_packet_sent(host, packet, ack);
- }
-
- while (!list_empty(&packet_list)) {
- packet = driver_packet(packet_list.next);
- list_del_init(&packet->driver_list);
- hpsb_packet_sent(host, packet, ACKX_ABORTED);
- }
-
- break;
-#if 0 /* has been removed from ieee1394 core */
- case ISO_LISTEN_CHANNEL:
- spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
-
- if (lynx->iso_rcv.chan_count++ == 0) {
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
- DMA_WORD1_CMP_ENABLE_MASTER);
- }
-
- spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
- break;
-
- case ISO_UNLISTEN_CHANNEL:
- spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
-
- if (--lynx->iso_rcv.chan_count == 0) {
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
- 0);
- }
-
- spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
- break;
-#endif
- default:
- PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd);
- retval = -1;
- }
-
- return retval;
-}
-
-
-/***************************************
- * IEEE-1394 functionality section END *
- ***************************************/
-
-
-/********************************************************
- * Global stuff (interrupt handler, init/shutdown code) *
- ********************************************************/
-
-
-static irqreturn_t lynx_irq_handler(int irq, void *dev_id)
-{
- struct ti_lynx *lynx = (struct ti_lynx *)dev_id;
- struct hpsb_host *host = lynx->host;
- u32 intmask;
- u32 linkint;
-
- linkint = reg_read(lynx, LINK_INT_STATUS);
- intmask = reg_read(lynx, PCI_INT_STATUS);
-
- if (!(intmask & PCI_INT_INT_PEND))
- return IRQ_NONE;
-
- PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask,
- linkint);
-
- reg_write(lynx, LINK_INT_STATUS, linkint);
- reg_write(lynx, PCI_INT_STATUS, intmask);
-
- if (intmask & PCI_INT_1394) {
- if (linkint & LINK_INT_PHY_TIMEOUT) {
- PRINT(KERN_INFO, lynx->id, "PHY timeout occurred");
- }
- if (linkint & LINK_INT_PHY_BUSRESET) {
- PRINT(KERN_INFO, lynx->id, "bus reset interrupt");
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
- if (!host->in_bus_reset)
- hpsb_bus_reset(host);
- }
- if (linkint & LINK_INT_PHY_REG_RCVD) {
- u32 reg;
-
- spin_lock(&lynx->phy_reg_lock);
- reg = reg_read(lynx, LINK_PHY);
- spin_unlock(&lynx->phy_reg_lock);
-
- if (!host->in_bus_reset) {
- PRINT(KERN_INFO, lynx->id,
- "phy reg received without reset");
- } else if (reg & 0xf00) {
- PRINT(KERN_INFO, lynx->id,
- "unsolicited phy reg %d received",
- (reg >> 8) & 0xf);
- } else {
- lynx->phy_reg0 = reg & 0xff;
- handle_selfid(lynx, host);
- }
- }
- if (linkint & LINK_INT_ISO_STUCK) {
- PRINT(KERN_INFO, lynx->id, "isochronous transmitter stuck");
- }
- if (linkint & LINK_INT_ASYNC_STUCK) {
- PRINT(KERN_INFO, lynx->id, "asynchronous transmitter stuck");
- }
- if (linkint & LINK_INT_SENT_REJECT) {
- PRINT(KERN_INFO, lynx->id, "sent reject");
- }
- if (linkint & LINK_INT_TX_INVALID_TC) {
- PRINT(KERN_INFO, lynx->id, "invalid transaction code");
- }
- if (linkint & LINK_INT_GRF_OVERFLOW) {
- /* flush FIFO if overflow happens during reset */
- if (host->in_bus_reset)
- reg_write(lynx, FIFO_CONTROL,
- FIFO_CONTROL_GRF_FLUSH);
- PRINT(KERN_INFO, lynx->id, "GRF overflow");
- }
- if (linkint & LINK_INT_ITF_UNDERFLOW) {
- PRINT(KERN_INFO, lynx->id, "ITF underflow");
- }
- if (linkint & LINK_INT_ATF_UNDERFLOW) {
- PRINT(KERN_INFO, lynx->id, "ATF underflow");
- }
- }
-
- if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) {
- PRINTD(KERN_DEBUG, lynx->id, "iso receive");
-
- spin_lock(&lynx->iso_rcv.lock);
-
- lynx->iso_rcv.stat[lynx->iso_rcv.next] =
- reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ISO_RCV));
-
- lynx->iso_rcv.used++;
- lynx->iso_rcv.next = (lynx->iso_rcv.next + 1) % NUM_ISORCV_PCL;
-
- if ((lynx->iso_rcv.next == lynx->iso_rcv.last)
- || !lynx->iso_rcv.chan_count) {
- PRINTD(KERN_DEBUG, lynx->id, "stopped");
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
- }
-
- run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, lynx->iso_rcv.next,
- CHANNEL_ISO_RCV);
-
- spin_unlock(&lynx->iso_rcv.lock);
-
- tasklet_schedule(&lynx->iso_rcv.tq);
- }
-
- if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) {
- PRINTD(KERN_DEBUG, lynx->id, "async sent");
- spin_lock(&lynx->async.queue_lock);
-
- if (list_empty(&lynx->async.pcl_queue)) {
- spin_unlock(&lynx->async.queue_lock);
- PRINT(KERN_WARNING, lynx->id, "async dma halted, but no queued packet (maybe it was cancelled)");
- } else {
- struct ti_pcl pcl;
- u32 ack;
- struct hpsb_packet *packet;
-
- get_pcl(lynx, lynx->async.pcl, &pcl);
-
- packet = driver_packet(lynx->async.pcl_queue.next);
- list_del_init(&packet->driver_list);
-
- pci_unmap_single(lynx->dev, lynx->async.header_dma,
- packet->header_size, PCI_DMA_TODEVICE);
- if (packet->data_size) {
- pci_unmap_single(lynx->dev, lynx->async.data_dma,
- packet->data_size, PCI_DMA_TODEVICE);
- }
-
- if (!list_empty(&lynx->async.queue)) {
- send_next(lynx, hpsb_async);
- }
-
- spin_unlock(&lynx->async.queue_lock);
-
- if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
- if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
- ack = (pcl.pcl_status >> 15) & 0xf;
- PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
- ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
- } else {
- ack = (pcl.pcl_status >> 15) & 0xf;
- }
- } else {
- PRINT(KERN_INFO, lynx->id, "async packet was not completed");
- ack = ACKX_SEND_ERROR;
- }
- hpsb_packet_sent(host, packet, ack);
- }
- }
-
- if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) {
- PRINTD(KERN_DEBUG, lynx->id, "iso sent");
- spin_lock(&lynx->iso_send.queue_lock);
-
- if (list_empty(&lynx->iso_send.pcl_queue)) {
- spin_unlock(&lynx->iso_send.queue_lock);
- PRINT(KERN_ERR, lynx->id, "iso send dma halted, but no queued packet");
- } else {
- struct ti_pcl pcl;
- u32 ack;
- struct hpsb_packet *packet;
-
- get_pcl(lynx, lynx->iso_send.pcl, &pcl);
-
- packet = driver_packet(lynx->iso_send.pcl_queue.next);
- list_del_init(&packet->driver_list);
-
- pci_unmap_single(lynx->dev, lynx->iso_send.header_dma,
- packet->header_size, PCI_DMA_TODEVICE);
- if (packet->data_size) {
- pci_unmap_single(lynx->dev, lynx->iso_send.data_dma,
- packet->data_size, PCI_DMA_TODEVICE);
- }
-#if 0 /* has been removed from ieee1394 core */
- if (!list_empty(&lynx->iso_send.queue)) {
- send_next(lynx, hpsb_iso);
- }
-#endif
- spin_unlock(&lynx->iso_send.queue_lock);
-
- if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
- if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
- ack = (pcl.pcl_status >> 15) & 0xf;
- PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
- ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
- } else {
- ack = (pcl.pcl_status >> 15) & 0xf;
- }
- } else {
- PRINT(KERN_INFO, lynx->id, "iso send packet was not completed");
- ack = ACKX_SEND_ERROR;
- }
-
- hpsb_packet_sent(host, packet, ack); //FIXME: maybe we should just use ACK_COMPLETE and ACKX_SEND_ERROR
- }
- }
-
- if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_RCV)) {
- /* general receive DMA completed */
- int stat = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_RCV));
-
- PRINTD(KERN_DEBUG, lynx->id, "received packet size %d",
- stat & 0x1fff);
-
- if (stat & DMA_CHAN_STAT_SELFID) {
- lynx->selfid_size = stat & 0x1fff;
- handle_selfid(lynx, host);
- } else {
- quadlet_t *q_data = lynx->rcv_page;
- if ((*q_data >> 4 & 0xf) == TCODE_READQ_RESPONSE
- || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) {
- cpu_to_be32s(q_data + 3);
- }
- hpsb_packet_received(host, q_data, stat & 0x1fff, 0);
- }
-
- run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
- }
-
- return IRQ_HANDLED;
-}
-
-
-static void iso_rcv_bh(struct ti_lynx *lynx)
-{
- unsigned int idx;
- quadlet_t *data;
- unsigned long flags;
-
- spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
-
- while (lynx->iso_rcv.used) {
- idx = lynx->iso_rcv.last;
- spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
-
- data = lynx->iso_rcv.page[idx / ISORCV_PER_PAGE]
- + (idx % ISORCV_PER_PAGE) * MAX_ISORCV_SIZE;
-
- if ((*data >> 16) + 4 != (lynx->iso_rcv.stat[idx] & 0x1fff)) {
- PRINT(KERN_ERR, lynx->id,
- "iso length mismatch 0x%08x/0x%08x", *data,
- lynx->iso_rcv.stat[idx]);
- }
-
- if (lynx->iso_rcv.stat[idx]
- & (DMA_CHAN_STAT_PCIERR | DMA_CHAN_STAT_PKTERR)) {
- PRINT(KERN_INFO, lynx->id,
- "iso receive error on %d to 0x%p", idx, data);
- } else {
- hpsb_packet_received(lynx->host, data,
- lynx->iso_rcv.stat[idx] & 0x1fff,
- 0);
- }
-
- spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
- lynx->iso_rcv.last = (idx + 1) % NUM_ISORCV_PCL;
- lynx->iso_rcv.used--;
- }
-
- if (lynx->iso_rcv.chan_count) {
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
- DMA_WORD1_CMP_ENABLE_MASTER);
- }
- spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
-}
-
-
-static void remove_card(struct pci_dev *dev)
-{
- struct ti_lynx *lynx;
- struct device *lynx_dev;
- int i;
-
- lynx = pci_get_drvdata(dev);
- if (!lynx) return;
- pci_set_drvdata(dev, NULL);
-
- lynx_dev = get_device(&lynx->host->device);
-
- switch (lynx->state) {
- case is_host:
- reg_write(lynx, PCI_INT_ENABLE, 0);
- hpsb_remove_host(lynx->host);
- case have_intr:
- reg_write(lynx, PCI_INT_ENABLE, 0);
- free_irq(lynx->dev->irq, lynx);
-
- /* Disable IRM Contender and LCtrl */
- if (lynx->phyic.reg_1394a)
- set_phy_reg(lynx, 4, ~0xc0 & get_phy_reg(lynx, 4));
-
- /* Let all other nodes know to ignore us */
- lynx_devctl(lynx->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
-
- case have_iomappings:
- reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
- /* Fix buggy cards with autoboot pin not tied low: */
- reg_write(lynx, DMA0_CHAN_CTRL, 0);
- iounmap(lynx->registers);
- iounmap(lynx->local_rom);
- iounmap(lynx->local_ram);
- iounmap(lynx->aux_port);
- case have_1394_buffers:
- for (i = 0; i < ISORCV_PAGES; i++) {
- if (lynx->iso_rcv.page[i]) {
- pci_free_consistent(lynx->dev, PAGE_SIZE,
- lynx->iso_rcv.page[i],
- lynx->iso_rcv.page_dma[i]);
- }
- }
- pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
- lynx->rcv_page_dma);
- case have_aux_buf:
- case have_pcl_mem:
- pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
- lynx->pcl_mem_dma);
- case clear:
- /* do nothing - already freed */
- ;
- }
-
- tasklet_kill(&lynx->iso_rcv.tq);
-
- if (lynx_dev)
- put_device(lynx_dev);
-}
-
-
-static int __devinit add_card(struct pci_dev *dev,
- const struct pci_device_id *devid_is_unused)
-{
-#define FAIL(fmt, args...) do { \
- PRINT_G(KERN_ERR, fmt , ## args); \
- remove_card(dev); \
- return error; \
- } while (0)
-
- char irq_buf[16];
- struct hpsb_host *host;
- struct ti_lynx *lynx; /* shortcut to currently handled device */
- struct ti_pcl pcl;
- u32 *pcli;
- int i;
- int error;
-
- error = -ENXIO;
-
- if (pci_set_dma_mask(dev, DMA_BIT_MASK(32)))
- FAIL("DMA address limits not supported for PCILynx hardware");
- if (pci_enable_device(dev))
- FAIL("failed to enable PCILynx hardware");
- pci_set_master(dev);
-
- error = -ENOMEM;
-
- host = hpsb_alloc_host(&lynx_driver, sizeof(struct ti_lynx), &dev->dev);
- if (!host) FAIL("failed to allocate control structure memory");
-
- lynx = host->hostdata;
- lynx->id = card_id++;
- lynx->dev = dev;
- lynx->state = clear;
- lynx->host = host;
- host->pdev = dev;
- pci_set_drvdata(dev, lynx);
-
- spin_lock_init(&lynx->lock);
- spin_lock_init(&lynx->phy_reg_lock);
-
- lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE,
- &lynx->pcl_mem_dma);
-
- if (lynx->pcl_mem != NULL) {
- lynx->state = have_pcl_mem;
- PRINT(KERN_INFO, lynx->id,
- "allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE,
- lynx->pcl_mem);
- } else {
- FAIL("failed to allocate PCL memory area");
- }
-
- lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE,
- &lynx->rcv_page_dma);
- if (lynx->rcv_page == NULL) {
- FAIL("failed to allocate receive buffer");
- }
- lynx->state = have_1394_buffers;
-
- for (i = 0; i < ISORCV_PAGES; i++) {
- lynx->iso_rcv.page[i] =
- pci_alloc_consistent(dev, PAGE_SIZE,
- &lynx->iso_rcv.page_dma[i]);
- if (lynx->iso_rcv.page[i] == NULL) {
- FAIL("failed to allocate iso receive buffers");
- }
- }
-
- lynx->registers = ioremap_nocache(pci_resource_start(dev,0),
- PCILYNX_MAX_REGISTER);
- lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY);
- lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY);
- lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE),
- PCILYNX_MAX_MEMORY);
- lynx->state = have_iomappings;
-
- if (lynx->registers == NULL) {
- FAIL("failed to remap registers - card not accessible");
- }
-
- reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
- /* Fix buggy cards with autoboot pin not tied low: */
- reg_write(lynx, DMA0_CHAN_CTRL, 0);
-
- sprintf (irq_buf, "%d", dev->irq);
-
- if (!request_irq(dev->irq, lynx_irq_handler, IRQF_SHARED,
- PCILYNX_DRIVER_NAME, lynx)) {
- PRINT(KERN_INFO, lynx->id, "allocated interrupt %s", irq_buf);
- lynx->state = have_intr;
- } else {
- FAIL("failed to allocate shared interrupt %s", irq_buf);
- }
-
- /* alloc_pcl return values are not checked, it is expected that the
- * provided PCL space is sufficient for the initial allocations */
- lynx->rcv_pcl = alloc_pcl(lynx);
- lynx->rcv_pcl_start = alloc_pcl(lynx);
- lynx->async.pcl = alloc_pcl(lynx);
- lynx->async.pcl_start = alloc_pcl(lynx);
- lynx->iso_send.pcl = alloc_pcl(lynx);
- lynx->iso_send.pcl_start = alloc_pcl(lynx);
-
- for (i = 0; i < NUM_ISORCV_PCL; i++) {
- lynx->iso_rcv.pcl[i] = alloc_pcl(lynx);
- }
- lynx->iso_rcv.pcl_start = alloc_pcl(lynx);
-
- /* all allocations successful - simple init stuff follows */
-
- reg_write(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL);
-
- tasklet_init(&lynx->iso_rcv.tq, (void (*)(unsigned long))iso_rcv_bh,
- (unsigned long)lynx);
-
- spin_lock_init(&lynx->iso_rcv.lock);
-
- spin_lock_init(&lynx->async.queue_lock);
- lynx->async.channel = CHANNEL_ASYNC_SEND;
- spin_lock_init(&lynx->iso_send.queue_lock);
- lynx->iso_send.channel = CHANNEL_ISO_SEND;
-
- PRINT(KERN_INFO, lynx->id, "remapped memory spaces reg 0x%p, rom 0x%p, "
- "ram 0x%p, aux 0x%p", lynx->registers, lynx->local_rom,
- lynx->local_ram, lynx->aux_port);
-
- /* now, looking for PHY register set */
- if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) {
- lynx->phyic.reg_1394a = 1;
- PRINT(KERN_INFO, lynx->id,
- "found 1394a conform PHY (using extended register set)");
- lynx->phyic.vendor = get_phy_vendorid(lynx);
- lynx->phyic.product = get_phy_productid(lynx);
- } else {
- lynx->phyic.reg_1394a = 0;
- PRINT(KERN_INFO, lynx->id, "found old 1394 PHY");
- }
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
-
- INIT_LIST_HEAD(&lynx->async.queue);
- INIT_LIST_HEAD(&lynx->async.pcl_queue);
- INIT_LIST_HEAD(&lynx->iso_send.queue);
- INIT_LIST_HEAD(&lynx->iso_send.pcl_queue);
-
- pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
- put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
-
- pcl.next = PCL_NEXT_INVALID;
- pcl.async_error_next = PCL_NEXT_INVALID;
-
- pcl.buffer[0].control = PCL_CMD_RCV | 16;
-#ifndef __BIG_ENDIAN
- pcl.buffer[0].control |= PCL_BIGENDIAN;
-#endif
- pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
-
- pcl.buffer[0].pointer = lynx->rcv_page_dma;
- pcl.buffer[1].pointer = lynx->rcv_page_dma + 16;
- put_pcl(lynx, lynx->rcv_pcl, &pcl);
-
- pcl.next = pcl_bus(lynx, lynx->async.pcl);
- pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl);
- put_pcl(lynx, lynx->async.pcl_start, &pcl);
-
- pcl.next = pcl_bus(lynx, lynx->iso_send.pcl);
- pcl.async_error_next = PCL_NEXT_INVALID;
- put_pcl(lynx, lynx->iso_send.pcl_start, &pcl);
-
- pcl.next = PCL_NEXT_INVALID;
- pcl.async_error_next = PCL_NEXT_INVALID;
- pcl.buffer[0].control = PCL_CMD_RCV | 4;
-#ifndef __BIG_ENDIAN
- pcl.buffer[0].control |= PCL_BIGENDIAN;
-#endif
- pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
-
- for (i = 0; i < NUM_ISORCV_PCL; i++) {
- int page = i / ISORCV_PER_PAGE;
- int sec = i % ISORCV_PER_PAGE;
-
- pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page]
- + sec * MAX_ISORCV_SIZE;
- pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
- put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
- }
-
- pcli = (u32 *)&pcl;
- for (i = 0; i < NUM_ISORCV_PCL; i++) {
- pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]);
- }
- put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl);
-
- /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */
- reg_write(lynx, FIFO_SIZES, 0x003030a0);
- /* 20 byte threshold before triggering PCI transfer */
- reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24);
- /* threshold on both send FIFOs before transmitting:
- FIFO size - cache line size - 1 */
- i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff;
- i = 0x30 - i - 1;
- reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i);
-
- reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394);
-
- reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT
- | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET
- | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK
- | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC
- | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW
- | LINK_INT_ATF_UNDERFLOW);
-
- reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
- reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4);
- reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV),
- DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST
- | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST
- | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER);
-
- run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
-
- reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0);
- reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4);
- reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0);
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
-
- run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV);
-
- reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID
- | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN
- | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN
- | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX);
-
- if (!lynx->phyic.reg_1394a) {
- if (!hpsb_disable_irm) {
- /* attempt to enable contender bit -FIXME- would this
- * work elsewhere? */
- reg_set_bits(lynx, GPIO_CTRL_A, 0x1);
- reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1);
- }
- } else {
- /* set the contender (if appropriate) and LCtrl bit in the
- * extended PHY register set. (Should check that PHY_02_EXTENDED
- * is set in register 2?)
- */
- i = get_phy_reg(lynx, 4);
- i |= PHY_04_LCTRL;
- if (hpsb_disable_irm)
- i &= ~PHY_04_CONTENDER;
- else
- i |= PHY_04_CONTENDER;
- if (i != -1) set_phy_reg(lynx, 4, i);
- }
-
- if (!skip_eeprom)
- {
- /* needed for i2c communication with serial eeprom */
- struct i2c_adapter *i2c_ad;
- struct i2c_algo_bit_data i2c_adapter_data;
-
- error = -ENOMEM;
- i2c_ad = kzalloc(sizeof(*i2c_ad), GFP_KERNEL);
- if (!i2c_ad) FAIL("failed to allocate I2C adapter memory");
-
- strlcpy(i2c_ad->name, "PCILynx I2C", sizeof(i2c_ad->name));
- i2c_adapter_data = bit_data;
- i2c_ad->algo_data = &i2c_adapter_data;
- i2c_adapter_data.data = lynx;
- i2c_ad->dev.parent = &dev->dev;
-
- PRINTD(KERN_DEBUG, lynx->id,"original eeprom control: %d",
- reg_read(lynx, SERIAL_EEPROM_CONTROL));
-
- /* reset hardware to sane state */
- lynx->i2c_driven_state = 0x00000070;
- reg_write(lynx, SERIAL_EEPROM_CONTROL, lynx->i2c_driven_state);
-
- if (i2c_bit_add_bus(i2c_ad) < 0)
- {
- kfree(i2c_ad);
- error = -ENXIO;
- FAIL("unable to register i2c");
- }
- else
- {
- /* do i2c stuff */
- unsigned char i2c_cmd = 0x10;
- struct i2c_msg msg[2] = { { 0x50, 0, 1, &i2c_cmd },
- { 0x50, I2C_M_RD, 20, (unsigned char*) lynx->bus_info_block }
- };
-
- /* we use i2c_transfer because we have no i2c_client
- at hand */
- if (i2c_transfer(i2c_ad, msg, 2) < 0) {
- PRINT(KERN_ERR, lynx->id, "unable to read bus info block from i2c");
- } else {
- PRINT(KERN_INFO, lynx->id, "got bus info block from serial eeprom");
- /* FIXME: probably we should rewrite the max_rec, max_ROM(1394a),
- * generation(1394a) and link_spd(1394a) field and recalculate
- * the CRC */
-
- for (i = 0; i < 5 ; i++)
- PRINTD(KERN_DEBUG, lynx->id, "Businfo block quadlet %i: %08x",
- i, be32_to_cpu(lynx->bus_info_block[i]));
-
- /* info_length, crc_length and 1394 magic number to check, if it is really a bus info block */
- if (((be32_to_cpu(lynx->bus_info_block[0]) & 0xffff0000) == 0x04040000) &&
- (lynx->bus_info_block[1] == IEEE1394_BUSID_MAGIC))
- {
- PRINT(KERN_DEBUG, lynx->id, "read a valid bus info block from");
- } else {
- kfree(i2c_ad);
- error = -ENXIO;
- FAIL("read something from serial eeprom, but it does not seem to be a valid bus info block");
- }
-
- }
-
- i2c_del_adapter(i2c_ad);
- kfree(i2c_ad);
- }
- }
-
- host->csr.guid_hi = be32_to_cpu(lynx->bus_info_block[3]);
- host->csr.guid_lo = be32_to_cpu(lynx->bus_info_block[4]);
- host->csr.cyc_clk_acc = (be32_to_cpu(lynx->bus_info_block[2]) >> 16) & 0xff;
- host->csr.max_rec = (be32_to_cpu(lynx->bus_info_block[2]) >> 12) & 0xf;
- if (!lynx->phyic.reg_1394a)
- host->csr.lnk_spd = (get_phy_reg(lynx, 2) & 0xc0) >> 6;
- else
- host->csr.lnk_spd = be32_to_cpu(lynx->bus_info_block[2]) & 0x7;
-
- if (hpsb_add_host(host)) {
- error = -ENOMEM;
- FAIL("Failed to register host with highlevel");
- }
-
- lynx->state = is_host;
-
- return 0;
-#undef FAIL
-}
-
-
-static struct pci_device_id pci_table[] = {
- {
- .vendor = PCI_VENDOR_ID_TI,
- .device = PCI_DEVICE_ID_TI_PCILYNX,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
- { } /* Terminating entry */
-};
-
-static struct pci_driver lynx_pci_driver = {
- .name = PCILYNX_DRIVER_NAME,
- .id_table = pci_table,
- .probe = add_card,
- .remove = remove_card,
-};
-
-static struct hpsb_host_driver lynx_driver = {
- .owner = THIS_MODULE,
- .name = PCILYNX_DRIVER_NAME,
- .set_hw_config_rom = NULL,
- .transmit_packet = lynx_transmit,
- .devctl = lynx_devctl,
- .isoctl = NULL,
-};
-
-MODULE_AUTHOR("Andreas E. Bombe <andreas.bombe@munich.netsurf.de>");
-MODULE_DESCRIPTION("driver for Texas Instruments PCI Lynx IEEE-1394 controller");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("pcilynx");
-MODULE_DEVICE_TABLE(pci, pci_table);
-
-static int __init pcilynx_init(void)
-{
- int ret;
-
- ret = pci_register_driver(&lynx_pci_driver);
- if (ret < 0) {
- PRINT_G(KERN_ERR, "PCI module init failed");
- return ret;
- }
-
- return 0;
-}
-
-static void __exit pcilynx_cleanup(void)
-{
- pci_unregister_driver(&lynx_pci_driver);
-}
-
-
-module_init(pcilynx_init);
-module_exit(pcilynx_cleanup);
diff --git a/drivers/ieee1394/pcilynx.h b/drivers/ieee1394/pcilynx.h
deleted file mode 100644
index 693a169acea3..000000000000
--- a/drivers/ieee1394/pcilynx.h
+++ /dev/null
@@ -1,468 +0,0 @@
-#ifndef __PCILYNX_H__
-#define __PCILYNX_H__
-
-
-#define PCILYNX_DRIVER_NAME "pcilynx"
-#define PCILYNX_MAJOR 177
-
-#define PCILYNX_MINOR_AUX_START 0
-#define PCILYNX_MINOR_ROM_START 16
-#define PCILYNX_MINOR_RAM_START 32
-
-#define PCILYNX_MAX_REGISTER 0xfff
-#define PCILYNX_MAX_MEMORY 0xffff
-
-#define PCI_DEVICE_ID_TI_PCILYNX 0x8000
-#define MAX_PCILYNX_CARDS 4
-#define LOCALRAM_SIZE 4096
-
-#define NUM_ISORCV_PCL 4
-#define MAX_ISORCV_SIZE 2048
-#define ISORCV_PER_PAGE (PAGE_SIZE / MAX_ISORCV_SIZE)
-#define ISORCV_PAGES (NUM_ISORCV_PCL / ISORCV_PER_PAGE)
-
-#define CHANNEL_LOCALBUS 0
-#define CHANNEL_ASYNC_RCV 1
-#define CHANNEL_ISO_RCV 2
-#define CHANNEL_ASYNC_SEND 3
-#define CHANNEL_ISO_SEND 4
-
-#define PCILYNX_CONFIG_ROM_LENGTH 1024
-
-typedef int pcl_t;
-
-struct ti_lynx {
- int id; /* sequential card number */
-
- spinlock_t lock;
-
- struct pci_dev *dev;
-
- struct {
- unsigned reg_1394a:1;
- u32 vendor;
- u32 product;
- } phyic;
-
- enum { clear, have_intr, have_aux_buf, have_pcl_mem,
- have_1394_buffers, have_iomappings, is_host } state;
-
- /* remapped memory spaces */
- void __iomem *registers;
- void __iomem *local_rom;
- void __iomem *local_ram;
- void __iomem *aux_port;
- __be32 bus_info_block[5];
-
- /*
- * use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for
- * LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes);
- * the following is an allocation bitmap
- */
- u8 pcl_bmap[LOCALRAM_SIZE / 1024];
-
- /* point to PCLs memory area if needed */
- void *pcl_mem;
- dma_addr_t pcl_mem_dma;
-
- /* PCLs for local mem / aux transfers */
- pcl_t dmem_pcl;
-
- /* IEEE-1394 part follows */
- struct hpsb_host *host;
-
- int phyid, isroot;
- int selfid_size;
- int phy_reg0;
-
- spinlock_t phy_reg_lock;
-
- pcl_t rcv_pcl_start, rcv_pcl;
- void *rcv_page;
- dma_addr_t rcv_page_dma;
- int rcv_active;
-
- struct lynx_send_data {
- pcl_t pcl_start, pcl;
- struct list_head queue;
- struct list_head pcl_queue; /* this queue contains at most one packet */
- spinlock_t queue_lock;
- dma_addr_t header_dma, data_dma;
- int channel;
- } async, iso_send;
-
- struct {
- pcl_t pcl[NUM_ISORCV_PCL];
- u32 stat[NUM_ISORCV_PCL];
- void *page[ISORCV_PAGES];
- dma_addr_t page_dma[ISORCV_PAGES];
- pcl_t pcl_start;
- int chan_count;
- int next, last, used, running;
- struct tasklet_struct tq;
- spinlock_t lock;
- } iso_rcv;
-
- u32 i2c_driven_state; /* the state we currently drive the Serial EEPROM Control register */
-};
-
-/* the per-file data structure for mem space access */
-struct memdata {
- struct ti_lynx *lynx;
- int cid;
- atomic_t aux_intr_last_seen;
- /* enum values are the same as LBUS_ADDR_SEL_* values below */
- enum { rom = 0x10000, aux = 0x20000, ram = 0 } type;
-};
-
-
-
-/*
- * Register read and write helper functions.
- */
-static inline void reg_write(const struct ti_lynx *lynx, int offset, u32 data)
-{
- writel(data, lynx->registers + offset);
-}
-
-static inline u32 reg_read(const struct ti_lynx *lynx, int offset)
-{
- return readl(lynx->registers + offset);
-}
-
-static inline void reg_set_bits(const struct ti_lynx *lynx, int offset,
- u32 mask)
-{
- reg_write(lynx, offset, (reg_read(lynx, offset) | mask));
-}
-
-static inline void reg_clear_bits(const struct ti_lynx *lynx, int offset,
- u32 mask)
-{
- reg_write(lynx, offset, (reg_read(lynx, offset) & ~mask));
-}
-
-
-
-/* chip register definitions follow */
-
-#define PCI_LATENCY_CACHELINE 0x0c
-
-#define MISC_CONTROL 0x40
-#define MISC_CONTROL_SWRESET (1<<0)
-
-#define SERIAL_EEPROM_CONTROL 0x44
-
-#define PCI_INT_STATUS 0x48
-#define PCI_INT_ENABLE 0x4c
-/* status and enable have identical bit numbers */
-#define PCI_INT_INT_PEND (1<<31)
-#define PCI_INT_FORCED_INT (1<<30)
-#define PCI_INT_SLV_ADR_PERR (1<<28)
-#define PCI_INT_SLV_DAT_PERR (1<<27)
-#define PCI_INT_MST_DAT_PERR (1<<26)
-#define PCI_INT_MST_DEV_TIMEOUT (1<<25)
-#define PCI_INT_INTERNAL_SLV_TIMEOUT (1<<23)
-#define PCI_INT_AUX_TIMEOUT (1<<18)
-#define PCI_INT_AUX_INT (1<<17)
-#define PCI_INT_1394 (1<<16)
-#define PCI_INT_DMA4_PCL (1<<9)
-#define PCI_INT_DMA4_HLT (1<<8)
-#define PCI_INT_DMA3_PCL (1<<7)
-#define PCI_INT_DMA3_HLT (1<<6)
-#define PCI_INT_DMA2_PCL (1<<5)
-#define PCI_INT_DMA2_HLT (1<<4)
-#define PCI_INT_DMA1_PCL (1<<3)
-#define PCI_INT_DMA1_HLT (1<<2)
-#define PCI_INT_DMA0_PCL (1<<1)
-#define PCI_INT_DMA0_HLT (1<<0)
-/* all DMA interrupts combined: */
-#define PCI_INT_DMA_ALL 0x3ff
-
-#define PCI_INT_DMA_HLT(chan) (1 << (chan * 2))
-#define PCI_INT_DMA_PCL(chan) (1 << (chan * 2 + 1))
-
-#define LBUS_ADDR 0xb4
-#define LBUS_ADDR_SEL_RAM (0x0<<16)
-#define LBUS_ADDR_SEL_ROM (0x1<<16)
-#define LBUS_ADDR_SEL_AUX (0x2<<16)
-#define LBUS_ADDR_SEL_ZV (0x3<<16)
-
-#define GPIO_CTRL_A 0xb8
-#define GPIO_CTRL_B 0xbc
-#define GPIO_DATA_BASE 0xc0
-
-#define DMA_BREG(base, chan) (base + chan * 0x20)
-#define DMA_SREG(base, chan) (base + chan * 0x10)
-
-#define DMA0_PREV_PCL 0x100
-#define DMA1_PREV_PCL 0x120
-#define DMA2_PREV_PCL 0x140
-#define DMA3_PREV_PCL 0x160
-#define DMA4_PREV_PCL 0x180
-#define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan))
-
-#define DMA0_CURRENT_PCL 0x104
-#define DMA1_CURRENT_PCL 0x124
-#define DMA2_CURRENT_PCL 0x144
-#define DMA3_CURRENT_PCL 0x164
-#define DMA4_CURRENT_PCL 0x184
-#define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan))
-
-#define DMA0_CHAN_STAT 0x10c
-#define DMA1_CHAN_STAT 0x12c
-#define DMA2_CHAN_STAT 0x14c
-#define DMA3_CHAN_STAT 0x16c
-#define DMA4_CHAN_STAT 0x18c
-#define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan))
-/* CHAN_STATUS registers share bits */
-#define DMA_CHAN_STAT_SELFID (1<<31)
-#define DMA_CHAN_STAT_ISOPKT (1<<30)
-#define DMA_CHAN_STAT_PCIERR (1<<29)
-#define DMA_CHAN_STAT_PKTERR (1<<28)
-#define DMA_CHAN_STAT_PKTCMPL (1<<27)
-#define DMA_CHAN_STAT_SPECIALACK (1<<14)
-
-
-#define DMA0_CHAN_CTRL 0x110
-#define DMA1_CHAN_CTRL 0x130
-#define DMA2_CHAN_CTRL 0x150
-#define DMA3_CHAN_CTRL 0x170
-#define DMA4_CHAN_CTRL 0x190
-#define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan))
-/* CHAN_CTRL registers share bits */
-#define DMA_CHAN_CTRL_ENABLE (1<<31)
-#define DMA_CHAN_CTRL_BUSY (1<<30)
-#define DMA_CHAN_CTRL_LINK (1<<29)
-
-#define DMA0_READY 0x114
-#define DMA1_READY 0x134
-#define DMA2_READY 0x154
-#define DMA3_READY 0x174
-#define DMA4_READY 0x194
-#define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan))
-
-#define DMA_GLOBAL_REGISTER 0x908
-
-#define FIFO_SIZES 0xa00
-
-#define FIFO_CONTROL 0xa10
-#define FIFO_CONTROL_GRF_FLUSH (1<<4)
-#define FIFO_CONTROL_ITF_FLUSH (1<<3)
-#define FIFO_CONTROL_ATF_FLUSH (1<<2)
-
-#define FIFO_XMIT_THRESHOLD 0xa14
-
-#define DMA0_WORD0_CMP_VALUE 0xb00
-#define DMA1_WORD0_CMP_VALUE 0xb10
-#define DMA2_WORD0_CMP_VALUE 0xb20
-#define DMA3_WORD0_CMP_VALUE 0xb30
-#define DMA4_WORD0_CMP_VALUE 0xb40
-#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan))
-
-#define DMA0_WORD0_CMP_ENABLE 0xb04
-#define DMA1_WORD0_CMP_ENABLE 0xb14
-#define DMA2_WORD0_CMP_ENABLE 0xb24
-#define DMA3_WORD0_CMP_ENABLE 0xb34
-#define DMA4_WORD0_CMP_ENABLE 0xb44
-#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE,chan))
-
-#define DMA0_WORD1_CMP_VALUE 0xb08
-#define DMA1_WORD1_CMP_VALUE 0xb18
-#define DMA2_WORD1_CMP_VALUE 0xb28
-#define DMA3_WORD1_CMP_VALUE 0xb38
-#define DMA4_WORD1_CMP_VALUE 0xb48
-#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan))
-
-#define DMA0_WORD1_CMP_ENABLE 0xb0c
-#define DMA1_WORD1_CMP_ENABLE 0xb1c
-#define DMA2_WORD1_CMP_ENABLE 0xb2c
-#define DMA3_WORD1_CMP_ENABLE 0xb3c
-#define DMA4_WORD1_CMP_ENABLE 0xb4c
-#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE,chan))
-/* word 1 compare enable flags */
-#define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15)
-#define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14)
-#define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13)
-#define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12)
-#define DMA_WORD1_CMP_MATCH_EXACT (1<<11)
-#define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10)
-#define DMA_WORD1_CMP_ENABLE_MASTER (1<<8)
-
-#define LINK_ID 0xf00
-#define LINK_ID_BUS(id) (id<<22)
-#define LINK_ID_NODE(id) (id<<16)
-
-#define LINK_CONTROL 0xf04
-#define LINK_CONTROL_BUSY (1<<29)
-#define LINK_CONTROL_TX_ISO_EN (1<<26)
-#define LINK_CONTROL_RX_ISO_EN (1<<25)
-#define LINK_CONTROL_TX_ASYNC_EN (1<<24)
-#define LINK_CONTROL_RX_ASYNC_EN (1<<23)
-#define LINK_CONTROL_RESET_TX (1<<21)
-#define LINK_CONTROL_RESET_RX (1<<20)
-#define LINK_CONTROL_CYCMASTER (1<<11)
-#define LINK_CONTROL_CYCSOURCE (1<<10)
-#define LINK_CONTROL_CYCTIMEREN (1<<9)
-#define LINK_CONTROL_RCV_CMP_VALID (1<<7)
-#define LINK_CONTROL_SNOOP_ENABLE (1<<6)
-
-#define CYCLE_TIMER 0xf08
-
-#define LINK_PHY 0xf0c
-#define LINK_PHY_READ (1<<31)
-#define LINK_PHY_WRITE (1<<30)
-#define LINK_PHY_ADDR(addr) (addr<<24)
-#define LINK_PHY_WDATA(data) (data<<16)
-#define LINK_PHY_RADDR(addr) (addr<<8)
-
-
-#define LINK_INT_STATUS 0xf14
-#define LINK_INT_ENABLE 0xf18
-/* status and enable have identical bit numbers */
-#define LINK_INT_LINK_INT (1<<31)
-#define LINK_INT_PHY_TIMEOUT (1<<30)
-#define LINK_INT_PHY_REG_RCVD (1<<29)
-#define LINK_INT_PHY_BUSRESET (1<<28)
-#define LINK_INT_TX_RDY (1<<26)
-#define LINK_INT_RX_DATA_RDY (1<<25)
-#define LINK_INT_ISO_STUCK (1<<20)
-#define LINK_INT_ASYNC_STUCK (1<<19)
-#define LINK_INT_SENT_REJECT (1<<17)
-#define LINK_INT_HDR_ERR (1<<16)
-#define LINK_INT_TX_INVALID_TC (1<<15)
-#define LINK_INT_CYC_SECOND (1<<11)
-#define LINK_INT_CYC_START (1<<10)
-#define LINK_INT_CYC_DONE (1<<9)
-#define LINK_INT_CYC_PENDING (1<<8)
-#define LINK_INT_CYC_LOST (1<<7)
-#define LINK_INT_CYC_ARB_FAILED (1<<6)
-#define LINK_INT_GRF_OVERFLOW (1<<5)
-#define LINK_INT_ITF_UNDERFLOW (1<<4)
-#define LINK_INT_ATF_UNDERFLOW (1<<3)
-#define LINK_INT_ISOARB_FAILED (1<<0)
-
-/* PHY specifics */
-#define PHY_VENDORID_TI 0x800028
-#define PHY_PRODUCTID_TSB41LV03 0x000000
-
-
-/* this is the physical layout of a PCL, its size is 128 bytes */
-struct ti_pcl {
- u32 next;
- u32 async_error_next;
- u32 user_data;
- u32 pcl_status;
- u32 remaining_transfer_count;
- u32 next_data_buffer;
- struct {
- u32 control;
- u32 pointer;
- } buffer[13] __attribute__ ((packed));
-} __attribute__ ((packed));
-
-#include <linux/stddef.h>
-#define pcloffs(MEMBER) (offsetof(struct ti_pcl, MEMBER))
-
-
-static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid,
- const struct ti_pcl *pcl)
-{
- memcpy_le32((u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)),
- (u32 *)pcl, sizeof(struct ti_pcl));
-}
-
-static inline void get_pcl(const struct ti_lynx *lynx, pcl_t pclid,
- struct ti_pcl *pcl)
-{
- memcpy_le32((u32 *)pcl,
- (u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)),
- sizeof(struct ti_pcl));
-}
-
-static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid)
-{
- return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl);
-}
-
-
-#if defined (__BIG_ENDIAN)
-typedef struct ti_pcl pcltmp_t;
-
-static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
- pcltmp_t *tmp)
-{
- get_pcl(lynx, pclid, tmp);
- return tmp;
-}
-
-static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
- pcltmp_t *tmp)
-{
- put_pcl(lynx, pclid, tmp);
-}
-
-#else
-typedef int pcltmp_t; /* just a dummy */
-
-static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
- pcltmp_t *tmp)
-{
- return lynx->pcl_mem + pclid * sizeof(struct ti_pcl);
-}
-
-static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
- pcltmp_t *tmp)
-{
-}
-#endif
-
-
-static inline void run_sub_pcl(const struct ti_lynx *lynx, pcl_t pclid, int idx,
- int dmachan)
-{
- reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20,
- pcl_bus(lynx, pclid) + idx * 4);
- reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20,
- DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK);
-}
-
-static inline void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan)
-{
- run_sub_pcl(lynx, pclid, 0, dmachan);
-}
-
-#define PCL_NEXT_INVALID (1<<0)
-
-/* transfer commands */
-#define PCL_CMD_RCV (0x1<<24)
-#define PCL_CMD_RCV_AND_UPDATE (0xa<<24)
-#define PCL_CMD_XMT (0x2<<24)
-#define PCL_CMD_UNFXMT (0xc<<24)
-#define PCL_CMD_PCI_TO_LBUS (0x8<<24)
-#define PCL_CMD_LBUS_TO_PCI (0x9<<24)
-
-/* aux commands */
-#define PCL_CMD_NOP (0x0<<24)
-#define PCL_CMD_LOAD (0x3<<24)
-#define PCL_CMD_STOREQ (0x4<<24)
-#define PCL_CMD_STORED (0xb<<24)
-#define PCL_CMD_STORE0 (0x5<<24)
-#define PCL_CMD_STORE1 (0x6<<24)
-#define PCL_CMD_COMPARE (0xe<<24)
-#define PCL_CMD_SWAP_COMPARE (0xf<<24)
-#define PCL_CMD_ADD (0xd<<24)
-#define PCL_CMD_BRANCH (0x7<<24)
-
-/* BRANCH condition codes */
-#define PCL_COND_DMARDY_SET (0x1<<20)
-#define PCL_COND_DMARDY_CLEAR (0x2<<20)
-
-#define PCL_GEN_INTR (1<<19)
-#define PCL_LAST_BUFF (1<<18)
-#define PCL_LAST_CMD (PCL_LAST_BUFF)
-#define PCL_WAITSTAT (1<<17)
-#define PCL_BIGENDIAN (1<<16)
-#define PCL_ISOMODE (1<<12)
-
-#endif
diff --git a/drivers/ieee1394/raw1394-private.h b/drivers/ieee1394/raw1394-private.h
deleted file mode 100644
index 7a225a405987..000000000000
--- a/drivers/ieee1394/raw1394-private.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#ifndef IEEE1394_RAW1394_PRIVATE_H
-#define IEEE1394_RAW1394_PRIVATE_H
-
-/* header for definitions that are private to the raw1394 driver
- and not visible to user-space */
-
-#define RAW1394_DEVICE_MAJOR 171
-#define RAW1394_DEVICE_NAME "raw1394"
-
-#define RAW1394_MAX_USER_CSR_DIRS 16
-
-struct iso_block_store {
- atomic_t refcount;
- size_t data_size;
- quadlet_t data[0];
-};
-
-enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0,
- RAW1394_ISO_RECV = 1,
- RAW1394_ISO_XMIT = 2 };
-
-struct file_info {
- struct list_head list;
-
- struct mutex state_mutex;
- enum { opened, initialized, connected } state;
- unsigned int protocol_version;
-
- struct hpsb_host *host;
-
- struct list_head req_pending; /* protected by reqlists_lock */
- struct list_head req_complete; /* protected by reqlists_lock */
- spinlock_t reqlists_lock;
- wait_queue_head_t wait_complete;
-
- struct list_head addr_list; /* protected by host_info_lock */
-
- u8 __user *fcp_buffer;
-
- u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */
-
- /* new rawiso API */
- enum raw1394_iso_state iso_state;
- struct hpsb_iso *iso_handle;
-
- /* User space's CSR1212 dynamic ConfigROM directories */
- struct csr1212_keyval *csr1212_dirs[RAW1394_MAX_USER_CSR_DIRS];
-
- /* Legacy ConfigROM update flag */
- u8 cfgrom_upd;
-};
-
-struct arm_addr {
- struct list_head addr_list; /* file_info list */
- u64 start, end;
- u64 arm_tag;
- u8 access_rights;
- u8 notification_options;
- u8 client_transactions;
- u64 recvb;
- u16 rec_length;
- u8 *addr_space_buffer; /* accessed by read/write/lock requests */
-};
-
-struct pending_request {
- struct list_head list;
- struct file_info *file_info;
- struct hpsb_packet *packet;
- struct iso_block_store *ibs;
- quadlet_t *data;
- int free_data;
- struct raw1394_request req;
-};
-
-struct host_info {
- struct list_head list;
- struct hpsb_host *host;
- struct list_head file_info_list; /* protected by host_info_lock */
-};
-
-#endif /* IEEE1394_RAW1394_PRIVATE_H */
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
deleted file mode 100644
index f3401427404c..000000000000
--- a/drivers/ieee1394/raw1394.c
+++ /dev/null
@@ -1,3096 +0,0 @@
-/*
- * IEEE 1394 for Linux
- *
- * Raw interface to the bus
- *
- * Copyright (C) 1999, 2000 Andreas E. Bombe
- * 2001, 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
- * 2002 Christian Toegel <christian.toegel@gmx.at>
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- *
- *
- * Contributions:
- *
- * Manfred Weihs <weihs@ict.tuwien.ac.at>
- * configuration ROM manipulation
- * address range mapping
- * adaptation for new (transparent) loopback mechanism
- * sending of arbitrary async packets
- * Christian Toegel <christian.toegel@gmx.at>
- * address range mapping
- * lock64 request
- * transmit physical packet
- * busreset notification control (switch on/off)
- * busreset with selection of type (short/long)
- * request_reply
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/vmalloc.h>
-#include <linux/cdev.h>
-#include <asm/uaccess.h>
-#include <asm/atomic.h>
-#include <linux/compat.h>
-
-#include "csr1212.h"
-#include "highlevel.h"
-#include "hosts.h"
-#include "ieee1394.h"
-#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
-#include "ieee1394_transactions.h"
-#include "ieee1394_types.h"
-#include "iso.h"
-#include "nodemgr.h"
-#include "raw1394.h"
-#include "raw1394-private.h"
-
-#define int2ptr(x) ((void __user *)(unsigned long)x)
-#define ptr2int(x) ((u64)(unsigned long)(void __user *)x)
-
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-#define RAW1394_DEBUG
-#endif
-
-#ifdef RAW1394_DEBUG
-#define DBGMSG(fmt, args...) \
-printk(KERN_INFO "raw1394:" fmt "\n" , ## args)
-#else
-#define DBGMSG(fmt, args...) do {} while (0)
-#endif
-
-static LIST_HEAD(host_info_list);
-static int host_count;
-static DEFINE_SPINLOCK(host_info_lock);
-static atomic_t internal_generation = ATOMIC_INIT(0);
-
-static atomic_t iso_buffer_size;
-static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */
-
-static struct hpsb_highlevel raw1394_highlevel;
-
-static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer,
- u64 addr, size_t length, u16 flags);
-static int arm_write(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t * data, u64 addr, size_t length, u16 flags);
-static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
- u16 flags);
-static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
- u16 flags);
-static const struct hpsb_address_ops arm_ops = {
- .read = arm_read,
- .write = arm_write,
- .lock = arm_lock,
- .lock64 = arm_lock64,
-};
-
-static void queue_complete_cb(struct pending_request *req);
-
-static struct pending_request *__alloc_pending_request(gfp_t flags)
-{
- struct pending_request *req;
-
- req = kzalloc(sizeof(*req), flags);
- if (req)
- INIT_LIST_HEAD(&req->list);
-
- return req;
-}
-
-static inline struct pending_request *alloc_pending_request(void)
-{
- return __alloc_pending_request(GFP_KERNEL);
-}
-
-static void free_pending_request(struct pending_request *req)
-{
- if (req->ibs) {
- if (atomic_dec_and_test(&req->ibs->refcount)) {
- atomic_sub(req->ibs->data_size, &iso_buffer_size);
- kfree(req->ibs);
- }
- } else if (req->free_data) {
- kfree(req->data);
- }
- hpsb_free_packet(req->packet);
- kfree(req);
-}
-
-/* fi->reqlists_lock must be taken */
-static void __queue_complete_req(struct pending_request *req)
-{
- struct file_info *fi = req->file_info;
-
- list_move_tail(&req->list, &fi->req_complete);
- wake_up(&fi->wait_complete);
-}
-
-static void queue_complete_req(struct pending_request *req)
-{
- unsigned long flags;
- struct file_info *fi = req->file_info;
-
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- __queue_complete_req(req);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-}
-
-static void queue_complete_cb(struct pending_request *req)
-{
- struct hpsb_packet *packet = req->packet;
- int rcode = (packet->header[1] >> 12) & 0xf;
-
- switch (packet->ack_code) {
- case ACKX_NONE:
- case ACKX_SEND_ERROR:
- req->req.error = RAW1394_ERROR_SEND_ERROR;
- break;
- case ACKX_ABORTED:
- req->req.error = RAW1394_ERROR_ABORTED;
- break;
- case ACKX_TIMEOUT:
- req->req.error = RAW1394_ERROR_TIMEOUT;
- break;
- default:
- req->req.error = (packet->ack_code << 16) | rcode;
- break;
- }
-
- if (!((packet->ack_code == ACK_PENDING) && (rcode == RCODE_COMPLETE))) {
- req->req.length = 0;
- }
-
- if ((req->req.type == RAW1394_REQ_ASYNC_READ) ||
- (req->req.type == RAW1394_REQ_ASYNC_WRITE) ||
- (req->req.type == RAW1394_REQ_ASYNC_STREAM) ||
- (req->req.type == RAW1394_REQ_LOCK) ||
- (req->req.type == RAW1394_REQ_LOCK64))
- hpsb_free_tlabel(packet);
-
- queue_complete_req(req);
-}
-
-static void add_host(struct hpsb_host *host)
-{
- struct host_info *hi;
- unsigned long flags;
-
- hi = kmalloc(sizeof(*hi), GFP_KERNEL);
-
- if (hi) {
- INIT_LIST_HEAD(&hi->list);
- hi->host = host;
- INIT_LIST_HEAD(&hi->file_info_list);
-
- spin_lock_irqsave(&host_info_lock, flags);
- list_add_tail(&hi->list, &host_info_list);
- host_count++;
- spin_unlock_irqrestore(&host_info_lock, flags);
- }
-
- atomic_inc(&internal_generation);
-}
-
-static struct host_info *find_host_info(struct hpsb_host *host)
-{
- struct host_info *hi;
-
- list_for_each_entry(hi, &host_info_list, list)
- if (hi->host == host)
- return hi;
-
- return NULL;
-}
-
-static void remove_host(struct hpsb_host *host)
-{
- struct host_info *hi;
- unsigned long flags;
-
- spin_lock_irqsave(&host_info_lock, flags);
- hi = find_host_info(host);
-
- if (hi != NULL) {
- list_del(&hi->list);
- host_count--;
- /*
- FIXME: address ranges should be removed
- and fileinfo states should be initialized
- (including setting generation to
- internal-generation ...)
- */
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- if (hi == NULL) {
- printk(KERN_ERR "raw1394: attempt to remove unknown host "
- "0x%p\n", host);
- return;
- }
-
- kfree(hi);
-
- atomic_inc(&internal_generation);
-}
-
-static void host_reset(struct hpsb_host *host)
-{
- unsigned long flags;
- struct host_info *hi;
- struct file_info *fi;
- struct pending_request *req;
-
- spin_lock_irqsave(&host_info_lock, flags);
- hi = find_host_info(host);
-
- if (hi != NULL) {
- list_for_each_entry(fi, &hi->file_info_list, list) {
- if (fi->notification == RAW1394_NOTIFY_ON) {
- req = __alloc_pending_request(GFP_ATOMIC);
-
- if (req != NULL) {
- req->file_info = fi;
- req->req.type = RAW1394_REQ_BUS_RESET;
- req->req.generation =
- get_hpsb_generation(host);
- req->req.misc = (host->node_id << 16)
- | host->node_count;
- if (fi->protocol_version > 3) {
- req->req.misc |=
- (NODEID_TO_NODE
- (host->irm_id)
- << 8);
- }
-
- queue_complete_req(req);
- }
- }
- }
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
-}
-
-static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
- int cts, u8 * data, size_t length)
-{
- unsigned long flags;
- struct host_info *hi;
- struct file_info *fi;
- struct pending_request *req, *req_next;
- struct iso_block_store *ibs = NULL;
- LIST_HEAD(reqs);
-
- if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) {
- HPSB_INFO("dropped fcp request");
- return;
- }
-
- spin_lock_irqsave(&host_info_lock, flags);
- hi = find_host_info(host);
-
- if (hi != NULL) {
- list_for_each_entry(fi, &hi->file_info_list, list) {
- if (!fi->fcp_buffer)
- continue;
-
- req = __alloc_pending_request(GFP_ATOMIC);
- if (!req)
- break;
-
- if (!ibs) {
- ibs = kmalloc(sizeof(*ibs) + length,
- GFP_ATOMIC);
- if (!ibs) {
- kfree(req);
- break;
- }
-
- atomic_add(length, &iso_buffer_size);
- atomic_set(&ibs->refcount, 0);
- ibs->data_size = length;
- memcpy(ibs->data, data, length);
- }
-
- atomic_inc(&ibs->refcount);
-
- req->file_info = fi;
- req->ibs = ibs;
- req->data = ibs->data;
- req->req.type = RAW1394_REQ_FCP_REQUEST;
- req->req.generation = get_hpsb_generation(host);
- req->req.misc = nodeid | (direction << 16);
- req->req.recvb = ptr2int(fi->fcp_buffer);
- req->req.length = length;
-
- list_add_tail(&req->list, &reqs);
- }
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- list_for_each_entry_safe(req, req_next, &reqs, list)
- queue_complete_req(req);
-}
-
-#ifdef CONFIG_COMPAT
-struct compat_raw1394_req {
- __u32 type;
- __s32 error;
- __u32 misc;
-
- __u32 generation;
- __u32 length;
-
- __u64 address;
-
- __u64 tag;
-
- __u64 sendb;
- __u64 recvb;
-}
-#if defined(CONFIG_X86_64) || defined(CONFIG_IA64)
-__attribute__((packed))
-#endif
-;
-
-static const char __user *raw1394_compat_write(const char __user *buf)
-{
- struct compat_raw1394_req __user *cr = (typeof(cr)) buf;
- struct raw1394_request __user *r;
-
- r = compat_alloc_user_space(sizeof(struct raw1394_request));
-
-#define C(x) __copy_in_user(&r->x, &cr->x, sizeof(r->x))
-
- if (copy_in_user(r, cr, sizeof(struct compat_raw1394_req)) ||
- C(address) ||
- C(tag) ||
- C(sendb) ||
- C(recvb))
- return (__force const char __user *)ERR_PTR(-EFAULT);
-
- return (const char __user *)r;
-}
-#undef C
-
-#define P(x) __put_user(r->x, &cr->x)
-
-static int
-raw1394_compat_read(const char __user *buf, struct raw1394_request *r)
-{
- struct compat_raw1394_req __user *cr = (typeof(cr)) buf;
-
- if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) ||
- P(type) ||
- P(error) ||
- P(misc) ||
- P(generation) ||
- P(length) ||
- P(address) ||
- P(tag) ||
- P(sendb) ||
- P(recvb))
- return -EFAULT;
-
- return sizeof(struct compat_raw1394_req);
-}
-#undef P
-
-#endif
-
-/* get next completed request (caller must hold fi->reqlists_lock) */
-static inline struct pending_request *__next_complete_req(struct file_info *fi)
-{
- struct list_head *lh;
- struct pending_request *req = NULL;
-
- if (!list_empty(&fi->req_complete)) {
- lh = fi->req_complete.next;
- list_del(lh);
- req = list_entry(lh, struct pending_request, list);
- }
- return req;
-}
-
-/* atomically get next completed request */
-static struct pending_request *next_complete_req(struct file_info *fi)
-{
- unsigned long flags;
- struct pending_request *req;
-
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- req = __next_complete_req(fi);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
- return req;
-}
-
-static ssize_t raw1394_read(struct file *file, char __user * buffer,
- size_t count, loff_t * offset_is_ignored)
-{
- struct file_info *fi = file->private_data;
- struct pending_request *req;
- ssize_t ret;
-
-#ifdef CONFIG_COMPAT
- if (count == sizeof(struct compat_raw1394_req)) {
- /* ok */
- } else
-#endif
- if (count != sizeof(struct raw1394_request)) {
- return -EINVAL;
- }
-
- if (!access_ok(VERIFY_WRITE, buffer, count)) {
- return -EFAULT;
- }
-
- if (file->f_flags & O_NONBLOCK) {
- if (!(req = next_complete_req(fi)))
- return -EAGAIN;
- } else {
- /*
- * NB: We call the macro wait_event_interruptible() with a
- * condition argument with side effect. This is only possible
- * because the side effect does not occur until the condition
- * became true, and wait_event_interruptible() won't evaluate
- * the condition again after that.
- */
- if (wait_event_interruptible(fi->wait_complete,
- (req = next_complete_req(fi))))
- return -ERESTARTSYS;
- }
-
- if (req->req.length) {
- if (copy_to_user(int2ptr(req->req.recvb), req->data,
- req->req.length)) {
- req->req.error = RAW1394_ERROR_MEMFAULT;
- }
- }
-
-#ifdef CONFIG_COMPAT
- if (count == sizeof(struct compat_raw1394_req) &&
- sizeof(struct compat_raw1394_req) !=
- sizeof(struct raw1394_request)) {
- ret = raw1394_compat_read(buffer, &req->req);
- } else
-#endif
- {
- if (copy_to_user(buffer, &req->req, sizeof(req->req))) {
- ret = -EFAULT;
- goto out;
- }
- ret = (ssize_t) sizeof(struct raw1394_request);
- }
- out:
- free_pending_request(req);
- return ret;
-}
-
-static int state_opened(struct file_info *fi, struct pending_request *req)
-{
- if (req->req.type == RAW1394_REQ_INITIALIZE) {
- switch (req->req.misc) {
- case RAW1394_KERNELAPI_VERSION:
- case 3:
- fi->state = initialized;
- fi->protocol_version = req->req.misc;
- req->req.error = RAW1394_ERROR_NONE;
- req->req.generation = atomic_read(&internal_generation);
- break;
-
- default:
- req->req.error = RAW1394_ERROR_COMPAT;
- req->req.misc = RAW1394_KERNELAPI_VERSION;
- }
- } else {
- req->req.error = RAW1394_ERROR_STATE_ORDER;
- }
-
- req->req.length = 0;
- queue_complete_req(req);
- return 0;
-}
-
-static int state_initialized(struct file_info *fi, struct pending_request *req)
-{
- unsigned long flags;
- struct host_info *hi;
- struct raw1394_khost_list *khl;
-
- if (req->req.generation != atomic_read(&internal_generation)) {
- req->req.error = RAW1394_ERROR_GENERATION;
- req->req.generation = atomic_read(&internal_generation);
- req->req.length = 0;
- queue_complete_req(req);
- return 0;
- }
-
- switch (req->req.type) {
- case RAW1394_REQ_LIST_CARDS:
- spin_lock_irqsave(&host_info_lock, flags);
- khl = kmalloc(sizeof(*khl) * host_count, GFP_ATOMIC);
-
- if (khl) {
- req->req.misc = host_count;
- req->data = (quadlet_t *) khl;
-
- list_for_each_entry(hi, &host_info_list, list) {
- khl->nodes = hi->host->node_count;
- strcpy(khl->name, hi->host->driver->name);
- khl++;
- }
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- if (khl) {
- req->req.error = RAW1394_ERROR_NONE;
- req->req.length = min(req->req.length,
- (u32) (sizeof
- (struct raw1394_khost_list)
- * req->req.misc));
- req->free_data = 1;
- } else {
- return -ENOMEM;
- }
- break;
-
- case RAW1394_REQ_SET_CARD:
- spin_lock_irqsave(&host_info_lock, flags);
- if (req->req.misc >= host_count) {
- req->req.error = RAW1394_ERROR_INVALID_ARG;
- goto out_set_card;
- }
- list_for_each_entry(hi, &host_info_list, list)
- if (!req->req.misc--)
- break;
- get_device(&hi->host->device); /* FIXME handle failure case */
- list_add_tail(&fi->list, &hi->file_info_list);
-
- /* prevent unloading of the host's low-level driver */
- if (!try_module_get(hi->host->driver->owner)) {
- req->req.error = RAW1394_ERROR_ABORTED;
- goto out_set_card;
- }
- WARN_ON(fi->host);
- fi->host = hi->host;
- fi->state = connected;
-
- req->req.error = RAW1394_ERROR_NONE;
- req->req.generation = get_hpsb_generation(fi->host);
- req->req.misc = (fi->host->node_id << 16)
- | fi->host->node_count;
- if (fi->protocol_version > 3)
- req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8;
-out_set_card:
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- req->req.length = 0;
- break;
-
- default:
- req->req.error = RAW1394_ERROR_STATE_ORDER;
- req->req.length = 0;
- break;
- }
-
- queue_complete_req(req);
- return 0;
-}
-
-static void handle_fcp_listen(struct file_info *fi, struct pending_request *req)
-{
- if (req->req.misc) {
- if (fi->fcp_buffer) {
- req->req.error = RAW1394_ERROR_ALREADY;
- } else {
- fi->fcp_buffer = int2ptr(req->req.recvb);
- }
- } else {
- if (!fi->fcp_buffer) {
- req->req.error = RAW1394_ERROR_ALREADY;
- } else {
- fi->fcp_buffer = NULL;
- }
- }
-
- req->req.length = 0;
- queue_complete_req(req);
-}
-
-static int handle_async_request(struct file_info *fi,
- struct pending_request *req, int node)
-{
- unsigned long flags;
- struct hpsb_packet *packet = NULL;
- u64 addr = req->req.address & 0xffffffffffffULL;
-
- switch (req->req.type) {
- case RAW1394_REQ_ASYNC_READ:
- DBGMSG("read_request called");
- packet =
- hpsb_make_readpacket(fi->host, node, addr, req->req.length);
-
- if (!packet)
- return -ENOMEM;
-
- if (req->req.length == 4)
- req->data = &packet->header[3];
- else
- req->data = packet->data;
-
- break;
-
- case RAW1394_REQ_ASYNC_WRITE:
- DBGMSG("write_request called");
-
- packet = hpsb_make_writepacket(fi->host, node, addr, NULL,
- req->req.length);
- if (!packet)
- return -ENOMEM;
-
- if (req->req.length == 4) {
- if (copy_from_user
- (&packet->header[3], int2ptr(req->req.sendb),
- req->req.length))
- req->req.error = RAW1394_ERROR_MEMFAULT;
- } else {
- if (copy_from_user
- (packet->data, int2ptr(req->req.sendb),
- req->req.length))
- req->req.error = RAW1394_ERROR_MEMFAULT;
- }
-
- req->req.length = 0;
- break;
-
- case RAW1394_REQ_ASYNC_STREAM:
- DBGMSG("stream_request called");
-
- packet =
- hpsb_make_streampacket(fi->host, NULL, req->req.length,
- node & 0x3f /*channel */ ,
- (req->req.misc >> 16) & 0x3,
- req->req.misc & 0xf);
- if (!packet)
- return -ENOMEM;
-
- if (copy_from_user(packet->data, int2ptr(req->req.sendb),
- req->req.length))
- req->req.error = RAW1394_ERROR_MEMFAULT;
-
- req->req.length = 0;
- break;
-
- case RAW1394_REQ_LOCK:
- DBGMSG("lock_request called");
- if ((req->req.misc == EXTCODE_FETCH_ADD)
- || (req->req.misc == EXTCODE_LITTLE_ADD)) {
- if (req->req.length != 4) {
- req->req.error = RAW1394_ERROR_INVALID_ARG;
- break;
- }
- } else {
- if (req->req.length != 8) {
- req->req.error = RAW1394_ERROR_INVALID_ARG;
- break;
- }
- }
-
- packet = hpsb_make_lockpacket(fi->host, node, addr,
- req->req.misc, NULL, 0);
- if (!packet)
- return -ENOMEM;
-
- if (copy_from_user(packet->data, int2ptr(req->req.sendb),
- req->req.length)) {
- req->req.error = RAW1394_ERROR_MEMFAULT;
- break;
- }
-
- req->data = packet->data;
- req->req.length = 4;
- break;
-
- case RAW1394_REQ_LOCK64:
- DBGMSG("lock64_request called");
- if ((req->req.misc == EXTCODE_FETCH_ADD)
- || (req->req.misc == EXTCODE_LITTLE_ADD)) {
- if (req->req.length != 8) {
- req->req.error = RAW1394_ERROR_INVALID_ARG;
- break;
- }
- } else {
- if (req->req.length != 16) {
- req->req.error = RAW1394_ERROR_INVALID_ARG;
- break;
- }
- }
- packet = hpsb_make_lock64packet(fi->host, node, addr,
- req->req.misc, NULL, 0);
- if (!packet)
- return -ENOMEM;
-
- if (copy_from_user(packet->data, int2ptr(req->req.sendb),
- req->req.length)) {
- req->req.error = RAW1394_ERROR_MEMFAULT;
- break;
- }
-
- req->data = packet->data;
- req->req.length = 8;
- break;
-
- default:
- req->req.error = RAW1394_ERROR_STATE_ORDER;
- }
-
- req->packet = packet;
-
- if (req->req.error) {
- req->req.length = 0;
- queue_complete_req(req);
- return 0;
- }
-
- hpsb_set_packet_complete_task(packet,
- (void (*)(void *))queue_complete_cb, req);
-
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- list_add_tail(&req->list, &fi->req_pending);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-
- packet->generation = req->req.generation;
-
- if (hpsb_send_packet(packet) < 0) {
- req->req.error = RAW1394_ERROR_SEND_ERROR;
- req->req.length = 0;
- hpsb_free_tlabel(packet);
- queue_complete_req(req);
- }
- return 0;
-}
-
-static int handle_async_send(struct file_info *fi, struct pending_request *req)
-{
- unsigned long flags;
- struct hpsb_packet *packet;
- int header_length = req->req.misc & 0xffff;
- int expect_response = req->req.misc >> 16;
- size_t data_size;
-
- if (header_length > req->req.length || header_length < 12 ||
- header_length > FIELD_SIZEOF(struct hpsb_packet, header)) {
- req->req.error = RAW1394_ERROR_INVALID_ARG;
- req->req.length = 0;
- queue_complete_req(req);
- return 0;
- }
-
- data_size = req->req.length - header_length;
- packet = hpsb_alloc_packet(data_size);
- req->packet = packet;
- if (!packet)
- return -ENOMEM;
-
- if (copy_from_user(packet->header, int2ptr(req->req.sendb),
- header_length)) {
- req->req.error = RAW1394_ERROR_MEMFAULT;
- req->req.length = 0;
- queue_complete_req(req);
- return 0;
- }
-
- if (copy_from_user
- (packet->data, int2ptr(req->req.sendb) + header_length,
- data_size)) {
- req->req.error = RAW1394_ERROR_MEMFAULT;
- req->req.length = 0;
- queue_complete_req(req);
- return 0;
- }
-
- packet->type = hpsb_async;
- packet->node_id = packet->header[0] >> 16;
- packet->tcode = (packet->header[0] >> 4) & 0xf;
- packet->tlabel = (packet->header[0] >> 10) & 0x3f;
- packet->host = fi->host;
- packet->expect_response = expect_response;
- packet->header_size = header_length;
- packet->data_size = data_size;
-
- req->req.length = 0;
- hpsb_set_packet_complete_task(packet,
- (void (*)(void *))queue_complete_cb, req);
-
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- list_add_tail(&req->list, &fi->req_pending);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-
- /* Update the generation of the packet just before sending. */
- packet->generation = req->req.generation;
-
- if (hpsb_send_packet(packet) < 0) {
- req->req.error = RAW1394_ERROR_SEND_ERROR;
- queue_complete_req(req);
- }
-
- return 0;
-}
-
-static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer,
- u64 addr, size_t length, u16 flags)
-{
- unsigned long irqflags;
- struct pending_request *req;
- struct host_info *hi;
- struct file_info *fi = NULL;
- struct list_head *entry;
- struct arm_addr *arm_addr = NULL;
- struct arm_request *arm_req = NULL;
- struct arm_response *arm_resp = NULL;
- int found = 0, size = 0, rcode = -1;
- struct arm_request_response *arm_req_resp = NULL;
-
- DBGMSG("arm_read called by node: %X "
- "addr: %4.4x %8.8x length: %Zu", nodeid,
- (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF),
- length);
- spin_lock_irqsave(&host_info_lock, irqflags);
- hi = find_host_info(host); /* search address-entry */
- if (hi != NULL) {
- list_for_each_entry(fi, &hi->file_info_list, list) {
- entry = fi->addr_list.next;
- while (entry != &(fi->addr_list)) {
- arm_addr =
- list_entry(entry, struct arm_addr,
- addr_list);
- if (((arm_addr->start) <= (addr))
- && ((arm_addr->end) >= (addr + length))) {
- found = 1;
- break;
- }
- entry = entry->next;
- }
- if (found) {
- break;
- }
- }
- }
- rcode = -1;
- if (!found) {
- printk(KERN_ERR "raw1394: arm_read FAILED addr_entry not found"
- " -> rcode_address_error\n");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_ADDRESS_ERROR);
- } else {
- DBGMSG("arm_read addr_entry FOUND");
- }
- if (arm_addr->rec_length < length) {
- DBGMSG("arm_read blocklength too big -> rcode_data_error");
- rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */
- }
- if (rcode == -1) {
- if (arm_addr->access_rights & ARM_READ) {
- if (!(arm_addr->client_transactions & ARM_READ)) {
- memcpy(buffer,
- (arm_addr->addr_space_buffer) + (addr -
- (arm_addr->
- start)),
- length);
- DBGMSG("arm_read -> (rcode_complete)");
- rcode = RCODE_COMPLETE;
- }
- } else {
- rcode = RCODE_TYPE_ERROR; /* function not allowed */
- DBGMSG("arm_read -> rcode_type_error (access denied)");
- }
- }
- if (arm_addr->notification_options & ARM_READ) {
- DBGMSG("arm_read -> entering notification-section");
- req = __alloc_pending_request(GFP_ATOMIC);
- if (!req) {
- DBGMSG("arm_read -> rcode_conflict_error");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request may be retried */
- }
- if (rcode == RCODE_COMPLETE) {
- size =
- sizeof(struct arm_request) +
- sizeof(struct arm_response) +
- length * sizeof(byte_t) +
- sizeof(struct arm_request_response);
- } else {
- size =
- sizeof(struct arm_request) +
- sizeof(struct arm_response) +
- sizeof(struct arm_request_response);
- }
- req->data = kmalloc(size, GFP_ATOMIC);
- if (!(req->data)) {
- free_pending_request(req);
- DBGMSG("arm_read -> rcode_conflict_error");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request may be retried */
- }
- req->free_data = 1;
- req->file_info = fi;
- req->req.type = RAW1394_REQ_ARM;
- req->req.generation = get_hpsb_generation(host);
- req->req.misc =
- (((length << 16) & (0xFFFF0000)) | (ARM_READ & 0xFF));
- req->req.tag = arm_addr->arm_tag;
- req->req.recvb = arm_addr->recvb;
- req->req.length = size;
- arm_req_resp = (struct arm_request_response *)(req->data);
- arm_req = (struct arm_request *)((byte_t *) (req->data) +
- (sizeof
- (struct
- arm_request_response)));
- arm_resp =
- (struct arm_response *)((byte_t *) (arm_req) +
- (sizeof(struct arm_request)));
- arm_req->buffer = NULL;
- arm_resp->buffer = NULL;
- if (rcode == RCODE_COMPLETE) {
- byte_t *buf =
- (byte_t *) arm_resp + sizeof(struct arm_response);
- memcpy(buf,
- (arm_addr->addr_space_buffer) + (addr -
- (arm_addr->
- start)),
- length);
- arm_resp->buffer =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request) +
- sizeof(struct arm_response));
- }
- arm_resp->buffer_length =
- (rcode == RCODE_COMPLETE) ? length : 0;
- arm_resp->response_code = rcode;
- arm_req->buffer_length = 0;
- arm_req->generation = req->req.generation;
- arm_req->extended_transaction_code = 0;
- arm_req->destination_offset = addr;
- arm_req->source_nodeid = nodeid;
- arm_req->destination_nodeid = host->node_id;
- arm_req->tlabel = (flags >> 10) & 0x3f;
- arm_req->tcode = (flags >> 4) & 0x0f;
- arm_req_resp->request = int2ptr((arm_addr->recvb) +
- sizeof(struct
- arm_request_response));
- arm_req_resp->response =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request));
- queue_complete_req(req);
- }
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (rcode);
-}
-
-static int arm_write(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t * data, u64 addr, size_t length, u16 flags)
-{
- unsigned long irqflags;
- struct pending_request *req;
- struct host_info *hi;
- struct file_info *fi = NULL;
- struct list_head *entry;
- struct arm_addr *arm_addr = NULL;
- struct arm_request *arm_req = NULL;
- struct arm_response *arm_resp = NULL;
- int found = 0, size = 0, rcode = -1;
- struct arm_request_response *arm_req_resp = NULL;
-
- DBGMSG("arm_write called by node: %X "
- "addr: %4.4x %8.8x length: %Zu", nodeid,
- (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF),
- length);
- spin_lock_irqsave(&host_info_lock, irqflags);
- hi = find_host_info(host); /* search address-entry */
- if (hi != NULL) {
- list_for_each_entry(fi, &hi->file_info_list, list) {
- entry = fi->addr_list.next;
- while (entry != &(fi->addr_list)) {
- arm_addr =
- list_entry(entry, struct arm_addr,
- addr_list);
- if (((arm_addr->start) <= (addr))
- && ((arm_addr->end) >= (addr + length))) {
- found = 1;
- break;
- }
- entry = entry->next;
- }
- if (found) {
- break;
- }
- }
- }
- rcode = -1;
- if (!found) {
- printk(KERN_ERR "raw1394: arm_write FAILED addr_entry not found"
- " -> rcode_address_error\n");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_ADDRESS_ERROR);
- } else {
- DBGMSG("arm_write addr_entry FOUND");
- }
- if (arm_addr->rec_length < length) {
- DBGMSG("arm_write blocklength too big -> rcode_data_error");
- rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */
- }
- if (rcode == -1) {
- if (arm_addr->access_rights & ARM_WRITE) {
- if (!(arm_addr->client_transactions & ARM_WRITE)) {
- memcpy((arm_addr->addr_space_buffer) +
- (addr - (arm_addr->start)), data,
- length);
- DBGMSG("arm_write -> (rcode_complete)");
- rcode = RCODE_COMPLETE;
- }
- } else {
- rcode = RCODE_TYPE_ERROR; /* function not allowed */
- DBGMSG("arm_write -> rcode_type_error (access denied)");
- }
- }
- if (arm_addr->notification_options & ARM_WRITE) {
- DBGMSG("arm_write -> entering notification-section");
- req = __alloc_pending_request(GFP_ATOMIC);
- if (!req) {
- DBGMSG("arm_write -> rcode_conflict_error");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request my be retried */
- }
- size =
- sizeof(struct arm_request) + sizeof(struct arm_response) +
- (length) * sizeof(byte_t) +
- sizeof(struct arm_request_response);
- req->data = kmalloc(size, GFP_ATOMIC);
- if (!(req->data)) {
- free_pending_request(req);
- DBGMSG("arm_write -> rcode_conflict_error");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request may be retried */
- }
- req->free_data = 1;
- req->file_info = fi;
- req->req.type = RAW1394_REQ_ARM;
- req->req.generation = get_hpsb_generation(host);
- req->req.misc =
- (((length << 16) & (0xFFFF0000)) | (ARM_WRITE & 0xFF));
- req->req.tag = arm_addr->arm_tag;
- req->req.recvb = arm_addr->recvb;
- req->req.length = size;
- arm_req_resp = (struct arm_request_response *)(req->data);
- arm_req = (struct arm_request *)((byte_t *) (req->data) +
- (sizeof
- (struct
- arm_request_response)));
- arm_resp =
- (struct arm_response *)((byte_t *) (arm_req) +
- (sizeof(struct arm_request)));
- arm_resp->buffer = NULL;
- memcpy((byte_t *) arm_resp + sizeof(struct arm_response),
- data, length);
- arm_req->buffer = int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request) +
- sizeof(struct arm_response));
- arm_req->buffer_length = length;
- arm_req->generation = req->req.generation;
- arm_req->extended_transaction_code = 0;
- arm_req->destination_offset = addr;
- arm_req->source_nodeid = nodeid;
- arm_req->destination_nodeid = destid;
- arm_req->tlabel = (flags >> 10) & 0x3f;
- arm_req->tcode = (flags >> 4) & 0x0f;
- arm_resp->buffer_length = 0;
- arm_resp->response_code = rcode;
- arm_req_resp->request = int2ptr((arm_addr->recvb) +
- sizeof(struct
- arm_request_response));
- arm_req_resp->response =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request));
- queue_complete_req(req);
- }
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (rcode);
-}
-
-static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
- u16 flags)
-{
- unsigned long irqflags;
- struct pending_request *req;
- struct host_info *hi;
- struct file_info *fi = NULL;
- struct list_head *entry;
- struct arm_addr *arm_addr = NULL;
- struct arm_request *arm_req = NULL;
- struct arm_response *arm_resp = NULL;
- int found = 0, size = 0, rcode = -1;
- quadlet_t old, new;
- struct arm_request_response *arm_req_resp = NULL;
-
- if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) ||
- ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) {
- DBGMSG("arm_lock called by node: %X "
- "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X",
- nodeid, (u16) ((addr >> 32) & 0xFFFF),
- (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF,
- be32_to_cpu(data));
- } else {
- DBGMSG("arm_lock called by node: %X "
- "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X arg: %8.8X",
- nodeid, (u16) ((addr >> 32) & 0xFFFF),
- (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF,
- be32_to_cpu(data), be32_to_cpu(arg));
- }
- spin_lock_irqsave(&host_info_lock, irqflags);
- hi = find_host_info(host); /* search address-entry */
- if (hi != NULL) {
- list_for_each_entry(fi, &hi->file_info_list, list) {
- entry = fi->addr_list.next;
- while (entry != &(fi->addr_list)) {
- arm_addr =
- list_entry(entry, struct arm_addr,
- addr_list);
- if (((arm_addr->start) <= (addr))
- && ((arm_addr->end) >=
- (addr + sizeof(*store)))) {
- found = 1;
- break;
- }
- entry = entry->next;
- }
- if (found) {
- break;
- }
- }
- }
- rcode = -1;
- if (!found) {
- printk(KERN_ERR "raw1394: arm_lock FAILED addr_entry not found"
- " -> rcode_address_error\n");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_ADDRESS_ERROR);
- } else {
- DBGMSG("arm_lock addr_entry FOUND");
- }
- if (rcode == -1) {
- if (arm_addr->access_rights & ARM_LOCK) {
- if (!(arm_addr->client_transactions & ARM_LOCK)) {
- memcpy(&old,
- (arm_addr->addr_space_buffer) + (addr -
- (arm_addr->
- start)),
- sizeof(old));
- switch (ext_tcode) {
- case (EXTCODE_MASK_SWAP):
- new = data | (old & ~arg);
- break;
- case (EXTCODE_COMPARE_SWAP):
- if (old == arg) {
- new = data;
- } else {
- new = old;
- }
- break;
- case (EXTCODE_FETCH_ADD):
- new =
- cpu_to_be32(be32_to_cpu(data) +
- be32_to_cpu(old));
- break;
- case (EXTCODE_LITTLE_ADD):
- new =
- cpu_to_le32(le32_to_cpu(data) +
- le32_to_cpu(old));
- break;
- case (EXTCODE_BOUNDED_ADD):
- if (old != arg) {
- new =
- cpu_to_be32(be32_to_cpu
- (data) +
- be32_to_cpu
- (old));
- } else {
- new = old;
- }
- break;
- case (EXTCODE_WRAP_ADD):
- if (old != arg) {
- new =
- cpu_to_be32(be32_to_cpu
- (data) +
- be32_to_cpu
- (old));
- } else {
- new = data;
- }
- break;
- default:
- rcode = RCODE_TYPE_ERROR; /* function not allowed */
- printk(KERN_ERR
- "raw1394: arm_lock FAILED "
- "ext_tcode not allowed -> rcode_type_error\n");
- break;
- } /*switch */
- if (rcode == -1) {
- DBGMSG("arm_lock -> (rcode_complete)");
- rcode = RCODE_COMPLETE;
- memcpy(store, &old, sizeof(*store));
- memcpy((arm_addr->addr_space_buffer) +
- (addr - (arm_addr->start)),
- &new, sizeof(*store));
- }
- }
- } else {
- rcode = RCODE_TYPE_ERROR; /* function not allowed */
- DBGMSG("arm_lock -> rcode_type_error (access denied)");
- }
- }
- if (arm_addr->notification_options & ARM_LOCK) {
- byte_t *buf1, *buf2;
- DBGMSG("arm_lock -> entering notification-section");
- req = __alloc_pending_request(GFP_ATOMIC);
- if (!req) {
- DBGMSG("arm_lock -> rcode_conflict_error");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request may be retried */
- }
- size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */
- req->data = kmalloc(size, GFP_ATOMIC);
- if (!(req->data)) {
- free_pending_request(req);
- DBGMSG("arm_lock -> rcode_conflict_error");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request may be retried */
- }
- req->free_data = 1;
- arm_req_resp = (struct arm_request_response *)(req->data);
- arm_req = (struct arm_request *)((byte_t *) (req->data) +
- (sizeof
- (struct
- arm_request_response)));
- arm_resp =
- (struct arm_response *)((byte_t *) (arm_req) +
- (sizeof(struct arm_request)));
- buf1 = (byte_t *) arm_resp + sizeof(struct arm_response);
- buf2 = buf1 + 2 * sizeof(*store);
- if ((ext_tcode == EXTCODE_FETCH_ADD) ||
- (ext_tcode == EXTCODE_LITTLE_ADD)) {
- arm_req->buffer_length = sizeof(*store);
- memcpy(buf1, &data, sizeof(*store));
-
- } else {
- arm_req->buffer_length = 2 * sizeof(*store);
- memcpy(buf1, &arg, sizeof(*store));
- memcpy(buf1 + sizeof(*store), &data, sizeof(*store));
- }
- if (rcode == RCODE_COMPLETE) {
- arm_resp->buffer_length = sizeof(*store);
- memcpy(buf2, &old, sizeof(*store));
- } else {
- arm_resp->buffer_length = 0;
- }
- req->file_info = fi;
- req->req.type = RAW1394_REQ_ARM;
- req->req.generation = get_hpsb_generation(host);
- req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) |
- (ARM_LOCK & 0xFF));
- req->req.tag = arm_addr->arm_tag;
- req->req.recvb = arm_addr->recvb;
- req->req.length = size;
- arm_req->generation = req->req.generation;
- arm_req->extended_transaction_code = ext_tcode;
- arm_req->destination_offset = addr;
- arm_req->source_nodeid = nodeid;
- arm_req->destination_nodeid = host->node_id;
- arm_req->tlabel = (flags >> 10) & 0x3f;
- arm_req->tcode = (flags >> 4) & 0x0f;
- arm_resp->response_code = rcode;
- arm_req_resp->request = int2ptr((arm_addr->recvb) +
- sizeof(struct
- arm_request_response));
- arm_req_resp->response =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request));
- arm_req->buffer =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request) +
- sizeof(struct arm_response));
- arm_resp->buffer =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request) +
- sizeof(struct arm_response) + 2 * sizeof(*store));
- queue_complete_req(req);
- }
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (rcode);
-}
-
-static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
- u16 flags)
-{
- unsigned long irqflags;
- struct pending_request *req;
- struct host_info *hi;
- struct file_info *fi = NULL;
- struct list_head *entry;
- struct arm_addr *arm_addr = NULL;
- struct arm_request *arm_req = NULL;
- struct arm_response *arm_resp = NULL;
- int found = 0, size = 0, rcode = -1;
- octlet_t old, new;
- struct arm_request_response *arm_req_resp = NULL;
-
- if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) ||
- ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) {
- DBGMSG("arm_lock64 called by node: %X "
- "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X ",
- nodeid, (u16) ((addr >> 32) & 0xFFFF),
- (u32) (addr & 0xFFFFFFFF),
- ext_tcode & 0xFF,
- (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF),
- (u32) (be64_to_cpu(data) & 0xFFFFFFFF));
- } else {
- DBGMSG("arm_lock64 called by node: %X "
- "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X arg: "
- "%8.8X %8.8X ",
- nodeid, (u16) ((addr >> 32) & 0xFFFF),
- (u32) (addr & 0xFFFFFFFF),
- ext_tcode & 0xFF,
- (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF),
- (u32) (be64_to_cpu(data) & 0xFFFFFFFF),
- (u32) ((be64_to_cpu(arg) >> 32) & 0xFFFFFFFF),
- (u32) (be64_to_cpu(arg) & 0xFFFFFFFF));
- }
- spin_lock_irqsave(&host_info_lock, irqflags);
- hi = find_host_info(host); /* search addressentry in file_info's for host */
- if (hi != NULL) {
- list_for_each_entry(fi, &hi->file_info_list, list) {
- entry = fi->addr_list.next;
- while (entry != &(fi->addr_list)) {
- arm_addr =
- list_entry(entry, struct arm_addr,
- addr_list);
- if (((arm_addr->start) <= (addr))
- && ((arm_addr->end) >=
- (addr + sizeof(*store)))) {
- found = 1;
- break;
- }
- entry = entry->next;
- }
- if (found) {
- break;
- }
- }
- }
- rcode = -1;
- if (!found) {
- printk(KERN_ERR
- "raw1394: arm_lock64 FAILED addr_entry not found"
- " -> rcode_address_error\n");
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (RCODE_ADDRESS_ERROR);
- } else {
- DBGMSG("arm_lock64 addr_entry FOUND");
- }
- if (rcode == -1) {
- if (arm_addr->access_rights & ARM_LOCK) {
- if (!(arm_addr->client_transactions & ARM_LOCK)) {
- memcpy(&old,
- (arm_addr->addr_space_buffer) + (addr -
- (arm_addr->
- start)),
- sizeof(old));
- switch (ext_tcode) {
- case (EXTCODE_MASK_SWAP):
- new = data | (old & ~arg);
- break;
- case (EXTCODE_COMPARE_SWAP):
- if (old == arg) {
- new = data;
- } else {
- new = old;
- }
- break;
- case (EXTCODE_FETCH_ADD):
- new =
- cpu_to_be64(be64_to_cpu(data) +
- be64_to_cpu(old));
- break;
- case (EXTCODE_LITTLE_ADD):
- new =
- cpu_to_le64(le64_to_cpu(data) +
- le64_to_cpu(old));
- break;
- case (EXTCODE_BOUNDED_ADD):
- if (old != arg) {
- new =
- cpu_to_be64(be64_to_cpu
- (data) +
- be64_to_cpu
- (old));
- } else {
- new = old;
- }
- break;
- case (EXTCODE_WRAP_ADD):
- if (old != arg) {
- new =
- cpu_to_be64(be64_to_cpu
- (data) +
- be64_to_cpu
- (old));
- } else {
- new = data;
- }
- break;
- default:
- printk(KERN_ERR
- "raw1394: arm_lock64 FAILED "
- "ext_tcode not allowed -> rcode_type_error\n");
- rcode = RCODE_TYPE_ERROR; /* function not allowed */
- break;
- } /*switch */
- if (rcode == -1) {
- DBGMSG
- ("arm_lock64 -> (rcode_complete)");
- rcode = RCODE_COMPLETE;
- memcpy(store, &old, sizeof(*store));
- memcpy((arm_addr->addr_space_buffer) +
- (addr - (arm_addr->start)),
- &new, sizeof(*store));
- }
- }
- } else {
- rcode = RCODE_TYPE_ERROR; /* function not allowed */
- DBGMSG
- ("arm_lock64 -> rcode_type_error (access denied)");
- }
- }
- if (arm_addr->notification_options & ARM_LOCK) {
- byte_t *buf1, *buf2;
- DBGMSG("arm_lock64 -> entering notification-section");
- req = __alloc_pending_request(GFP_ATOMIC);
- if (!req) {
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- DBGMSG("arm_lock64 -> rcode_conflict_error");
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request may be retried */
- }
- size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */
- req->data = kmalloc(size, GFP_ATOMIC);
- if (!(req->data)) {
- free_pending_request(req);
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- DBGMSG("arm_lock64 -> rcode_conflict_error");
- return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
- The request may be retried */
- }
- req->free_data = 1;
- arm_req_resp = (struct arm_request_response *)(req->data);
- arm_req = (struct arm_request *)((byte_t *) (req->data) +
- (sizeof
- (struct
- arm_request_response)));
- arm_resp =
- (struct arm_response *)((byte_t *) (arm_req) +
- (sizeof(struct arm_request)));
- buf1 = (byte_t *) arm_resp + sizeof(struct arm_response);
- buf2 = buf1 + 2 * sizeof(*store);
- if ((ext_tcode == EXTCODE_FETCH_ADD) ||
- (ext_tcode == EXTCODE_LITTLE_ADD)) {
- arm_req->buffer_length = sizeof(*store);
- memcpy(buf1, &data, sizeof(*store));
-
- } else {
- arm_req->buffer_length = 2 * sizeof(*store);
- memcpy(buf1, &arg, sizeof(*store));
- memcpy(buf1 + sizeof(*store), &data, sizeof(*store));
- }
- if (rcode == RCODE_COMPLETE) {
- arm_resp->buffer_length = sizeof(*store);
- memcpy(buf2, &old, sizeof(*store));
- } else {
- arm_resp->buffer_length = 0;
- }
- req->file_info = fi;
- req->req.type = RAW1394_REQ_ARM;
- req->req.generation = get_hpsb_generation(host);
- req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) |
- (ARM_LOCK & 0xFF));
- req->req.tag = arm_addr->arm_tag;
- req->req.recvb = arm_addr->recvb;
- req->req.length = size;
- arm_req->generation = req->req.generation;
- arm_req->extended_transaction_code = ext_tcode;
- arm_req->destination_offset = addr;
- arm_req->source_nodeid = nodeid;
- arm_req->destination_nodeid = host->node_id;
- arm_req->tlabel = (flags >> 10) & 0x3f;
- arm_req->tcode = (flags >> 4) & 0x0f;
- arm_resp->response_code = rcode;
- arm_req_resp->request = int2ptr((arm_addr->recvb) +
- sizeof(struct
- arm_request_response));
- arm_req_resp->response =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request));
- arm_req->buffer =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request) +
- sizeof(struct arm_response));
- arm_resp->buffer =
- int2ptr((arm_addr->recvb) +
- sizeof(struct arm_request_response) +
- sizeof(struct arm_request) +
- sizeof(struct arm_response) + 2 * sizeof(*store));
- queue_complete_req(req);
- }
- spin_unlock_irqrestore(&host_info_lock, irqflags);
- return (rcode);
-}
-
-static int arm_register(struct file_info *fi, struct pending_request *req)
-{
- int retval;
- struct arm_addr *addr;
- struct host_info *hi;
- struct file_info *fi_hlp = NULL;
- struct list_head *entry;
- struct arm_addr *arm_addr = NULL;
- int same_host, another_host;
- unsigned long flags;
-
- DBGMSG("arm_register called "
- "addr(Offset): %8.8x %8.8x length: %u "
- "rights: %2.2X notify: %2.2X "
- "max_blk_len: %4.4X",
- (u32) ((req->req.address >> 32) & 0xFFFF),
- (u32) (req->req.address & 0xFFFFFFFF),
- req->req.length, ((req->req.misc >> 8) & 0xFF),
- (req->req.misc & 0xFF), ((req->req.misc >> 16) & 0xFFFF));
- /* check addressrange */
- if ((((req->req.address) & ~(0xFFFFFFFFFFFFULL)) != 0) ||
- (((req->req.address + req->req.length) & ~(0xFFFFFFFFFFFFULL)) !=
- 0)) {
- req->req.length = 0;
- return (-EINVAL);
- }
- /* addr-list-entry for fileinfo */
- addr = kmalloc(sizeof(*addr), GFP_KERNEL);
- if (!addr) {
- req->req.length = 0;
- return (-ENOMEM);
- }
- /* allocation of addr_space_buffer */
- addr->addr_space_buffer = vmalloc(req->req.length);
- if (!(addr->addr_space_buffer)) {
- kfree(addr);
- req->req.length = 0;
- return (-ENOMEM);
- }
- /* initialization of addr_space_buffer */
- if ((req->req.sendb) == (unsigned long)NULL) {
- /* init: set 0 */
- memset(addr->addr_space_buffer, 0, req->req.length);
- } else {
- /* init: user -> kernel */
- if (copy_from_user
- (addr->addr_space_buffer, int2ptr(req->req.sendb),
- req->req.length)) {
- vfree(addr->addr_space_buffer);
- kfree(addr);
- return (-EFAULT);
- }
- }
- INIT_LIST_HEAD(&addr->addr_list);
- addr->arm_tag = req->req.tag;
- addr->start = req->req.address;
- addr->end = req->req.address + req->req.length;
- addr->access_rights = (u8) (req->req.misc & 0x0F);
- addr->notification_options = (u8) ((req->req.misc >> 4) & 0x0F);
- addr->client_transactions = (u8) ((req->req.misc >> 8) & 0x0F);
- addr->access_rights |= addr->client_transactions;
- addr->notification_options |= addr->client_transactions;
- addr->recvb = req->req.recvb;
- addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF);
-
- spin_lock_irqsave(&host_info_lock, flags);
- hi = find_host_info(fi->host);
- same_host = 0;
- another_host = 0;
- /* same host with address-entry containing same addressrange ? */
- list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
- entry = fi_hlp->addr_list.next;
- while (entry != &(fi_hlp->addr_list)) {
- arm_addr =
- list_entry(entry, struct arm_addr, addr_list);
- if ((arm_addr->start == addr->start)
- && (arm_addr->end == addr->end)) {
- DBGMSG("same host ownes same "
- "addressrange -> EALREADY");
- same_host = 1;
- break;
- }
- entry = entry->next;
- }
- if (same_host) {
- break;
- }
- }
- if (same_host) {
- /* addressrange occupied by same host */
- spin_unlock_irqrestore(&host_info_lock, flags);
- vfree(addr->addr_space_buffer);
- kfree(addr);
- return (-EALREADY);
- }
- /* another host with valid address-entry containing same addressrange */
- list_for_each_entry(hi, &host_info_list, list) {
- if (hi->host != fi->host) {
- list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
- entry = fi_hlp->addr_list.next;
- while (entry != &(fi_hlp->addr_list)) {
- arm_addr =
- list_entry(entry, struct arm_addr,
- addr_list);
- if ((arm_addr->start == addr->start)
- && (arm_addr->end == addr->end)) {
- DBGMSG
- ("another host ownes same "
- "addressrange");
- another_host = 1;
- break;
- }
- entry = entry->next;
- }
- if (another_host) {
- break;
- }
- }
- }
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- if (another_host) {
- DBGMSG("another hosts entry is valid -> SUCCESS");
- if (copy_to_user(int2ptr(req->req.recvb),
- &addr->start, sizeof(u64))) {
- printk(KERN_ERR "raw1394: arm_register failed "
- " address-range-entry is invalid -> EFAULT !!!\n");
- vfree(addr->addr_space_buffer);
- kfree(addr);
- return (-EFAULT);
- }
- free_pending_request(req); /* immediate success or fail */
- /* INSERT ENTRY */
- spin_lock_irqsave(&host_info_lock, flags);
- list_add_tail(&addr->addr_list, &fi->addr_list);
- spin_unlock_irqrestore(&host_info_lock, flags);
- return 0;
- }
- retval =
- hpsb_register_addrspace(&raw1394_highlevel, fi->host, &arm_ops,
- req->req.address,
- req->req.address + req->req.length);
- if (retval) {
- /* INSERT ENTRY */
- spin_lock_irqsave(&host_info_lock, flags);
- list_add_tail(&addr->addr_list, &fi->addr_list);
- spin_unlock_irqrestore(&host_info_lock, flags);
- } else {
- DBGMSG("arm_register failed errno: %d \n", retval);
- vfree(addr->addr_space_buffer);
- kfree(addr);
- return (-EALREADY);
- }
- free_pending_request(req); /* immediate success or fail */
- return 0;
-}
-
-static int arm_unregister(struct file_info *fi, struct pending_request *req)
-{
- int found = 0;
- int retval = 0;
- struct list_head *entry;
- struct arm_addr *addr = NULL;
- struct host_info *hi;
- struct file_info *fi_hlp = NULL;
- struct arm_addr *arm_addr = NULL;
- int another_host;
- unsigned long flags;
-
- DBGMSG("arm_Unregister called addr(Offset): "
- "%8.8x %8.8x",
- (u32) ((req->req.address >> 32) & 0xFFFF),
- (u32) (req->req.address & 0xFFFFFFFF));
- spin_lock_irqsave(&host_info_lock, flags);
- /* get addr */
- entry = fi->addr_list.next;
- while (entry != &(fi->addr_list)) {
- addr = list_entry(entry, struct arm_addr, addr_list);
- if (addr->start == req->req.address) {
- found = 1;
- break;
- }
- entry = entry->next;
- }
- if (!found) {
- DBGMSG("arm_Unregister addr not found");
- spin_unlock_irqrestore(&host_info_lock, flags);
- return (-EINVAL);
- }
- DBGMSG("arm_Unregister addr found");
- another_host = 0;
- /* another host with valid address-entry containing
- same addressrange */
- list_for_each_entry(hi, &host_info_list, list) {
- if (hi->host != fi->host) {
- list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
- entry = fi_hlp->addr_list.next;
- while (entry != &(fi_hlp->addr_list)) {
- arm_addr = list_entry(entry,
- struct arm_addr,
- addr_list);
- if (arm_addr->start == addr->start) {
- DBGMSG("another host ownes "
- "same addressrange");
- another_host = 1;
- break;
- }
- entry = entry->next;
- }
- if (another_host) {
- break;
- }
- }
- }
- }
- if (another_host) {
- DBGMSG("delete entry from list -> success");
- list_del(&addr->addr_list);
- spin_unlock_irqrestore(&host_info_lock, flags);
- vfree(addr->addr_space_buffer);
- kfree(addr);
- free_pending_request(req); /* immediate success or fail */
- return 0;
- }
- retval =
- hpsb_unregister_addrspace(&raw1394_highlevel, fi->host,
- addr->start);
- if (!retval) {
- printk(KERN_ERR "raw1394: arm_Unregister failed -> EINVAL\n");
- spin_unlock_irqrestore(&host_info_lock, flags);
- return (-EINVAL);
- }
- DBGMSG("delete entry from list -> success");
- list_del(&addr->addr_list);
- spin_unlock_irqrestore(&host_info_lock, flags);
- vfree(addr->addr_space_buffer);
- kfree(addr);
- free_pending_request(req); /* immediate success or fail */
- return 0;
-}
-
-/* Copy data from ARM buffer(s) to user buffer. */
-static int arm_get_buf(struct file_info *fi, struct pending_request *req)
-{
- struct arm_addr *arm_addr = NULL;
- unsigned long flags;
- unsigned long offset;
-
- struct list_head *entry;
-
- DBGMSG("arm_get_buf "
- "addr(Offset): %04X %08X length: %u",
- (u32) ((req->req.address >> 32) & 0xFFFF),
- (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length);
-
- spin_lock_irqsave(&host_info_lock, flags);
- entry = fi->addr_list.next;
- while (entry != &(fi->addr_list)) {
- arm_addr = list_entry(entry, struct arm_addr, addr_list);
- if ((arm_addr->start <= req->req.address) &&
- (arm_addr->end > req->req.address)) {
- if (req->req.address + req->req.length <= arm_addr->end) {
- offset = req->req.address - arm_addr->start;
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- DBGMSG
- ("arm_get_buf copy_to_user( %08X, %p, %u )",
- (u32) req->req.recvb,
- arm_addr->addr_space_buffer + offset,
- (u32) req->req.length);
- if (copy_to_user
- (int2ptr(req->req.recvb),
- arm_addr->addr_space_buffer + offset,
- req->req.length))
- return (-EFAULT);
-
- /* We have to free the request, because we
- * queue no response, and therefore nobody
- * will free it. */
- free_pending_request(req);
- return 0;
- } else {
- DBGMSG("arm_get_buf request exceeded mapping");
- spin_unlock_irqrestore(&host_info_lock, flags);
- return (-EINVAL);
- }
- }
- entry = entry->next;
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
- return (-EINVAL);
-}
-
-/* Copy data from user buffer to ARM buffer(s). */
-static int arm_set_buf(struct file_info *fi, struct pending_request *req)
-{
- struct arm_addr *arm_addr = NULL;
- unsigned long flags;
- unsigned long offset;
-
- struct list_head *entry;
-
- DBGMSG("arm_set_buf "
- "addr(Offset): %04X %08X length: %u",
- (u32) ((req->req.address >> 32) & 0xFFFF),
- (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length);
-
- spin_lock_irqsave(&host_info_lock, flags);
- entry = fi->addr_list.next;
- while (entry != &(fi->addr_list)) {
- arm_addr = list_entry(entry, struct arm_addr, addr_list);
- if ((arm_addr->start <= req->req.address) &&
- (arm_addr->end > req->req.address)) {
- if (req->req.address + req->req.length <= arm_addr->end) {
- offset = req->req.address - arm_addr->start;
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- DBGMSG
- ("arm_set_buf copy_from_user( %p, %08X, %u )",
- arm_addr->addr_space_buffer + offset,
- (u32) req->req.sendb,
- (u32) req->req.length);
- if (copy_from_user
- (arm_addr->addr_space_buffer + offset,
- int2ptr(req->req.sendb),
- req->req.length))
- return (-EFAULT);
-
- /* We have to free the request, because we
- * queue no response, and therefore nobody
- * will free it. */
- free_pending_request(req);
- return 0;
- } else {
- DBGMSG("arm_set_buf request exceeded mapping");
- spin_unlock_irqrestore(&host_info_lock, flags);
- return (-EINVAL);
- }
- }
- entry = entry->next;
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
- return (-EINVAL);
-}
-
-static int reset_notification(struct file_info *fi, struct pending_request *req)
-{
- DBGMSG("reset_notification called - switch %s ",
- (req->req.misc == RAW1394_NOTIFY_OFF) ? "OFF" : "ON");
- if ((req->req.misc == RAW1394_NOTIFY_OFF) ||
- (req->req.misc == RAW1394_NOTIFY_ON)) {
- fi->notification = (u8) req->req.misc;
- free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
- return 0;
- }
- /* error EINVAL (22) invalid argument */
- return (-EINVAL);
-}
-
-static int write_phypacket(struct file_info *fi, struct pending_request *req)
-{
- struct hpsb_packet *packet = NULL;
- int retval = 0;
- quadlet_t data;
- unsigned long flags;
-
- data = be32_to_cpu((u32) req->req.sendb);
- DBGMSG("write_phypacket called - quadlet 0x%8.8x ", data);
- packet = hpsb_make_phypacket(fi->host, data);
- if (!packet)
- return -ENOMEM;
- req->req.length = 0;
- req->packet = packet;
- hpsb_set_packet_complete_task(packet,
- (void (*)(void *))queue_complete_cb, req);
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- list_add_tail(&req->list, &fi->req_pending);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
- packet->generation = req->req.generation;
- retval = hpsb_send_packet(packet);
- DBGMSG("write_phypacket send_packet called => retval: %d ", retval);
- if (retval < 0) {
- req->req.error = RAW1394_ERROR_SEND_ERROR;
- req->req.length = 0;
- queue_complete_req(req);
- }
- return 0;
-}
-
-static int get_config_rom(struct file_info *fi, struct pending_request *req)
-{
- int ret = 0;
- quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL);
- int status;
-
- if (!data)
- return -ENOMEM;
-
- status =
- csr1212_read(fi->host->csr.rom, CSR1212_CONFIG_ROM_SPACE_OFFSET,
- data, req->req.length);
- if (copy_to_user(int2ptr(req->req.recvb), data, req->req.length))
- ret = -EFAULT;
- if (copy_to_user
- (int2ptr(req->req.tag), &fi->host->csr.rom->cache_head->len,
- sizeof(fi->host->csr.rom->cache_head->len)))
- ret = -EFAULT;
- if (copy_to_user(int2ptr(req->req.address), &fi->host->csr.generation,
- sizeof(fi->host->csr.generation)))
- ret = -EFAULT;
- if (copy_to_user(int2ptr(req->req.sendb), &status, sizeof(status)))
- ret = -EFAULT;
- kfree(data);
- if (ret >= 0) {
- free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
- }
- return ret;
-}
-
-static int update_config_rom(struct file_info *fi, struct pending_request *req)
-{
- int ret = 0;
- quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- if (copy_from_user(data, int2ptr(req->req.sendb), req->req.length)) {
- ret = -EFAULT;
- } else {
- int status = hpsb_update_config_rom(fi->host,
- data, req->req.length,
- (unsigned char)req->req.
- misc);
- if (copy_to_user
- (int2ptr(req->req.recvb), &status, sizeof(status)))
- ret = -ENOMEM;
- }
- kfree(data);
- if (ret >= 0) {
- free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
- fi->cfgrom_upd = 1;
- }
- return ret;
-}
-
-static int modify_config_rom(struct file_info *fi, struct pending_request *req)
-{
- struct csr1212_keyval *kv;
- struct csr1212_csr_rom_cache *cache;
- struct csr1212_dentry *dentry;
- u32 dr;
- int ret = 0;
-
- if (req->req.misc == ~0) {
- if (req->req.length == 0)
- return -EINVAL;
-
- /* Find an unused slot */
- for (dr = 0;
- dr < RAW1394_MAX_USER_CSR_DIRS && fi->csr1212_dirs[dr];
- dr++) ;
-
- if (dr == RAW1394_MAX_USER_CSR_DIRS)
- return -ENOMEM;
-
- fi->csr1212_dirs[dr] =
- csr1212_new_directory(CSR1212_KV_ID_VENDOR);
- if (!fi->csr1212_dirs[dr])
- return -ENOMEM;
- } else {
- dr = req->req.misc;
- if (!fi->csr1212_dirs[dr])
- return -EINVAL;
-
- /* Delete old stuff */
- for (dentry =
- fi->csr1212_dirs[dr]->value.directory.dentries_head;
- dentry; dentry = dentry->next) {
- csr1212_detach_keyval_from_directory(fi->host->csr.rom->
- root_kv,
- dentry->kv);
- }
-
- if (req->req.length == 0) {
- csr1212_release_keyval(fi->csr1212_dirs[dr]);
- fi->csr1212_dirs[dr] = NULL;
-
- hpsb_update_config_rom_image(fi->host);
- free_pending_request(req);
- return 0;
- }
- }
-
- cache = csr1212_rom_cache_malloc(0, req->req.length);
- if (!cache) {
- csr1212_release_keyval(fi->csr1212_dirs[dr]);
- fi->csr1212_dirs[dr] = NULL;
- return -ENOMEM;
- }
-
- cache->filled_head = kmalloc(sizeof(*cache->filled_head), GFP_KERNEL);
- if (!cache->filled_head) {
- csr1212_release_keyval(fi->csr1212_dirs[dr]);
- fi->csr1212_dirs[dr] = NULL;
- CSR1212_FREE(cache);
- return -ENOMEM;
- }
- cache->filled_tail = cache->filled_head;
-
- if (copy_from_user(cache->data, int2ptr(req->req.sendb),
- req->req.length)) {
- csr1212_release_keyval(fi->csr1212_dirs[dr]);
- fi->csr1212_dirs[dr] = NULL;
- ret = -EFAULT;
- } else {
- cache->len = req->req.length;
- cache->filled_head->offset_start = 0;
- cache->filled_head->offset_end = cache->size - 1;
-
- cache->layout_head = cache->layout_tail = fi->csr1212_dirs[dr];
-
- ret = CSR1212_SUCCESS;
- /* parse all the items */
- for (kv = cache->layout_head; ret == CSR1212_SUCCESS && kv;
- kv = kv->next) {
- ret = csr1212_parse_keyval(kv, cache);
- }
-
- /* attach top level items to the root directory */
- for (dentry =
- fi->csr1212_dirs[dr]->value.directory.dentries_head;
- ret == CSR1212_SUCCESS && dentry; dentry = dentry->next) {
- ret =
- csr1212_attach_keyval_to_directory(fi->host->csr.
- rom->root_kv,
- dentry->kv);
- }
-
- if (ret == CSR1212_SUCCESS) {
- ret = hpsb_update_config_rom_image(fi->host);
-
- if (ret >= 0 && copy_to_user(int2ptr(req->req.recvb),
- &dr, sizeof(dr))) {
- ret = -ENOMEM;
- }
- }
- }
- kfree(cache->filled_head);
- CSR1212_FREE(cache);
-
- if (ret >= 0) {
- /* we have to free the request, because we queue no response,
- * and therefore nobody will free it */
- free_pending_request(req);
- return 0;
- } else {
- for (dentry =
- fi->csr1212_dirs[dr]->value.directory.dentries_head;
- dentry; dentry = dentry->next) {
- csr1212_detach_keyval_from_directory(fi->host->csr.rom->
- root_kv,
- dentry->kv);
- }
- csr1212_release_keyval(fi->csr1212_dirs[dr]);
- fi->csr1212_dirs[dr] = NULL;
- return ret;
- }
-}
-
-static int state_connected(struct file_info *fi, struct pending_request *req)
-{
- int node = req->req.address >> 48;
-
- req->req.error = RAW1394_ERROR_NONE;
-
- switch (req->req.type) {
-
- case RAW1394_REQ_ECHO:
- queue_complete_req(req);
- return 0;
-
- case RAW1394_REQ_ARM_REGISTER:
- return arm_register(fi, req);
-
- case RAW1394_REQ_ARM_UNREGISTER:
- return arm_unregister(fi, req);
-
- case RAW1394_REQ_ARM_SET_BUF:
- return arm_set_buf(fi, req);
-
- case RAW1394_REQ_ARM_GET_BUF:
- return arm_get_buf(fi, req);
-
- case RAW1394_REQ_RESET_NOTIFY:
- return reset_notification(fi, req);
-
- case RAW1394_REQ_ISO_SEND:
- case RAW1394_REQ_ISO_LISTEN:
- printk(KERN_DEBUG "raw1394: old iso ABI has been removed\n");
- req->req.error = RAW1394_ERROR_COMPAT;
- req->req.misc = RAW1394_KERNELAPI_VERSION;
- queue_complete_req(req);
- return 0;
-
- case RAW1394_REQ_FCP_LISTEN:
- handle_fcp_listen(fi, req);
- return 0;
-
- case RAW1394_REQ_RESET_BUS:
- if (req->req.misc == RAW1394_LONG_RESET) {
- DBGMSG("busreset called (type: LONG)");
- hpsb_reset_bus(fi->host, LONG_RESET);
- free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
- return 0;
- }
- if (req->req.misc == RAW1394_SHORT_RESET) {
- DBGMSG("busreset called (type: SHORT)");
- hpsb_reset_bus(fi->host, SHORT_RESET);
- free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
- return 0;
- }
- /* error EINVAL (22) invalid argument */
- return (-EINVAL);
- case RAW1394_REQ_GET_ROM:
- return get_config_rom(fi, req);
-
- case RAW1394_REQ_UPDATE_ROM:
- return update_config_rom(fi, req);
-
- case RAW1394_REQ_MODIFY_ROM:
- return modify_config_rom(fi, req);
- }
-
- if (req->req.generation != get_hpsb_generation(fi->host)) {
- req->req.error = RAW1394_ERROR_GENERATION;
- req->req.generation = get_hpsb_generation(fi->host);
- req->req.length = 0;
- queue_complete_req(req);
- return 0;
- }
-
- switch (req->req.type) {
- case RAW1394_REQ_PHYPACKET:
- return write_phypacket(fi, req);
- case RAW1394_REQ_ASYNC_SEND:
- return handle_async_send(fi, req);
- }
-
- if (req->req.length == 0) {
- req->req.error = RAW1394_ERROR_INVALID_ARG;
- queue_complete_req(req);
- return 0;
- }
-
- return handle_async_request(fi, req, node);
-}
-
-static ssize_t raw1394_write(struct file *file, const char __user * buffer,
- size_t count, loff_t * offset_is_ignored)
-{
- struct file_info *fi = file->private_data;
- struct pending_request *req;
- ssize_t retval = -EBADFD;
-
-#ifdef CONFIG_COMPAT
- if (count == sizeof(struct compat_raw1394_req) &&
- sizeof(struct compat_raw1394_req) !=
- sizeof(struct raw1394_request)) {
- buffer = raw1394_compat_write(buffer);
- if (IS_ERR((__force void *)buffer))
- return PTR_ERR((__force void *)buffer);
- } else
-#endif
- if (count != sizeof(struct raw1394_request)) {
- return -EINVAL;
- }
-
- req = alloc_pending_request();
- if (req == NULL) {
- return -ENOMEM;
- }
- req->file_info = fi;
-
- if (copy_from_user(&req->req, buffer, sizeof(struct raw1394_request))) {
- free_pending_request(req);
- return -EFAULT;
- }
-
- if (!mutex_trylock(&fi->state_mutex)) {
- free_pending_request(req);
- return -EAGAIN;
- }
-
- switch (fi->state) {
- case opened:
- retval = state_opened(fi, req);
- break;
-
- case initialized:
- retval = state_initialized(fi, req);
- break;
-
- case connected:
- retval = state_connected(fi, req);
- break;
- }
-
- mutex_unlock(&fi->state_mutex);
-
- if (retval < 0) {
- free_pending_request(req);
- } else {
- BUG_ON(retval);
- retval = count;
- }
-
- return retval;
-}
-
-/* rawiso operations */
-
-/* check if any RAW1394_REQ_RAWISO_ACTIVITY event is already in the
- * completion queue (reqlists_lock must be taken) */
-static inline int __rawiso_event_in_queue(struct file_info *fi)
-{
- struct pending_request *req;
-
- list_for_each_entry(req, &fi->req_complete, list)
- if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY)
- return 1;
-
- return 0;
-}
-
-/* put a RAWISO_ACTIVITY event in the queue, if one isn't there already */
-static void queue_rawiso_event(struct file_info *fi)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&fi->reqlists_lock, flags);
-
- /* only one ISO activity event may be in the queue */
- if (!__rawiso_event_in_queue(fi)) {
- struct pending_request *req =
- __alloc_pending_request(GFP_ATOMIC);
-
- if (req) {
- req->file_info = fi;
- req->req.type = RAW1394_REQ_RAWISO_ACTIVITY;
- req->req.generation = get_hpsb_generation(fi->host);
- __queue_complete_req(req);
- } else {
- /* on allocation failure, signal an overflow */
- if (fi->iso_handle) {
- atomic_inc(&fi->iso_handle->overflows);
- }
- }
- }
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-}
-
-static void rawiso_activity_cb(struct hpsb_iso *iso)
-{
- unsigned long flags;
- struct host_info *hi;
- struct file_info *fi;
-
- spin_lock_irqsave(&host_info_lock, flags);
- hi = find_host_info(iso->host);
-
- if (hi != NULL) {
- list_for_each_entry(fi, &hi->file_info_list, list) {
- if (fi->iso_handle == iso)
- queue_rawiso_event(fi);
- }
- }
-
- spin_unlock_irqrestore(&host_info_lock, flags);
-}
-
-/* helper function - gather all the kernel iso status bits for returning to user-space */
-static void raw1394_iso_fill_status(struct hpsb_iso *iso,
- struct raw1394_iso_status *stat)
-{
- int overflows = atomic_read(&iso->overflows);
- int skips = atomic_read(&iso->skips);
-
- stat->config.data_buf_size = iso->buf_size;
- stat->config.buf_packets = iso->buf_packets;
- stat->config.channel = iso->channel;
- stat->config.speed = iso->speed;
- stat->config.irq_interval = iso->irq_interval;
- stat->n_packets = hpsb_iso_n_ready(iso);
- stat->overflows = ((skips & 0xFFFF) << 16) | ((overflows & 0xFFFF));
- stat->xmit_cycle = iso->xmit_cycle;
-}
-
-static int raw1394_iso_xmit_init(struct file_info *fi, void __user * uaddr)
-{
- struct raw1394_iso_status stat;
-
- if (!fi->host)
- return -EINVAL;
-
- if (copy_from_user(&stat, uaddr, sizeof(stat)))
- return -EFAULT;
-
- fi->iso_handle = hpsb_iso_xmit_init(fi->host,
- stat.config.data_buf_size,
- stat.config.buf_packets,
- stat.config.channel,
- stat.config.speed,
- stat.config.irq_interval,
- rawiso_activity_cb);
- if (!fi->iso_handle)
- return -ENOMEM;
-
- fi->iso_state = RAW1394_ISO_XMIT;
-
- raw1394_iso_fill_status(fi->iso_handle, &stat);
- if (copy_to_user(uaddr, &stat, sizeof(stat)))
- return -EFAULT;
-
- /* queue an event to get things started */
- rawiso_activity_cb(fi->iso_handle);
-
- return 0;
-}
-
-static int raw1394_iso_recv_init(struct file_info *fi, void __user * uaddr)
-{
- struct raw1394_iso_status stat;
-
- if (!fi->host)
- return -EINVAL;
-
- if (copy_from_user(&stat, uaddr, sizeof(stat)))
- return -EFAULT;
-
- fi->iso_handle = hpsb_iso_recv_init(fi->host,
- stat.config.data_buf_size,
- stat.config.buf_packets,
- stat.config.channel,
- stat.config.dma_mode,
- stat.config.irq_interval,
- rawiso_activity_cb);
- if (!fi->iso_handle)
- return -ENOMEM;
-
- fi->iso_state = RAW1394_ISO_RECV;
-
- raw1394_iso_fill_status(fi->iso_handle, &stat);
- if (copy_to_user(uaddr, &stat, sizeof(stat)))
- return -EFAULT;
- return 0;
-}
-
-static int raw1394_iso_get_status(struct file_info *fi, void __user * uaddr)
-{
- struct raw1394_iso_status stat;
- struct hpsb_iso *iso = fi->iso_handle;
-
- raw1394_iso_fill_status(fi->iso_handle, &stat);
- if (copy_to_user(uaddr, &stat, sizeof(stat)))
- return -EFAULT;
-
- /* reset overflow counter */
- atomic_set(&iso->overflows, 0);
- /* reset skip counter */
- atomic_set(&iso->skips, 0);
-
- return 0;
-}
-
-/* copy N packet_infos out of the ringbuffer into user-supplied array */
-static int raw1394_iso_recv_packets(struct file_info *fi, void __user * uaddr)
-{
- struct raw1394_iso_packets upackets;
- unsigned int packet = fi->iso_handle->first_packet;
- int i;
-
- if (copy_from_user(&upackets, uaddr, sizeof(upackets)))
- return -EFAULT;
-
- if (upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle))
- return -EINVAL;
-
- /* ensure user-supplied buffer is accessible and big enough */
- if (!access_ok(VERIFY_WRITE, upackets.infos,
- upackets.n_packets *
- sizeof(struct raw1394_iso_packet_info)))
- return -EFAULT;
-
- /* copy the packet_infos out */
- for (i = 0; i < upackets.n_packets; i++) {
- if (__copy_to_user(&upackets.infos[i],
- &fi->iso_handle->infos[packet],
- sizeof(struct raw1394_iso_packet_info)))
- return -EFAULT;
-
- packet = (packet + 1) % fi->iso_handle->buf_packets;
- }
-
- return 0;
-}
-
-/* copy N packet_infos from user to ringbuffer, and queue them for transmission */
-static int raw1394_iso_send_packets(struct file_info *fi, void __user * uaddr)
-{
- struct raw1394_iso_packets upackets;
- int i, rv;
-
- if (copy_from_user(&upackets, uaddr, sizeof(upackets)))
- return -EFAULT;
-
- if (upackets.n_packets >= fi->iso_handle->buf_packets)
- return -EINVAL;
-
- if (upackets.n_packets >= hpsb_iso_n_ready(fi->iso_handle))
- return -EAGAIN;
-
- /* ensure user-supplied buffer is accessible and big enough */
- if (!access_ok(VERIFY_READ, upackets.infos,
- upackets.n_packets *
- sizeof(struct raw1394_iso_packet_info)))
- return -EFAULT;
-
- /* copy the infos structs in and queue the packets */
- for (i = 0; i < upackets.n_packets; i++) {
- struct raw1394_iso_packet_info info;
-
- if (__copy_from_user(&info, &upackets.infos[i],
- sizeof(struct raw1394_iso_packet_info)))
- return -EFAULT;
-
- rv = hpsb_iso_xmit_queue_packet(fi->iso_handle, info.offset,
- info.len, info.tag, info.sy);
- if (rv)
- return rv;
- }
-
- return 0;
-}
-
-static void raw1394_iso_shutdown(struct file_info *fi)
-{
- if (fi->iso_handle)
- hpsb_iso_shutdown(fi->iso_handle);
-
- fi->iso_handle = NULL;
- fi->iso_state = RAW1394_ISO_INACTIVE;
-}
-
-static int raw1394_read_cycle_timer(struct file_info *fi, void __user * uaddr)
-{
- struct raw1394_cycle_timer ct;
- int err;
-
- err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time);
- if (!err)
- if (copy_to_user(uaddr, &ct, sizeof(ct)))
- err = -EFAULT;
- return err;
-}
-
-/* mmap the rawiso xmit/recv buffer */
-static int raw1394_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct file_info *fi = file->private_data;
- int ret;
-
- if (!mutex_trylock(&fi->state_mutex))
- return -EAGAIN;
-
- if (fi->iso_state == RAW1394_ISO_INACTIVE)
- ret = -EINVAL;
- else
- ret = dma_region_mmap(&fi->iso_handle->data_buf, file, vma);
-
- mutex_unlock(&fi->state_mutex);
-
- return ret;
-}
-
-static long raw1394_ioctl_inactive(struct file_info *fi, unsigned int cmd,
- void __user *argp)
-{
- switch (cmd) {
- case RAW1394_IOC_ISO_XMIT_INIT:
- return raw1394_iso_xmit_init(fi, argp);
- case RAW1394_IOC_ISO_RECV_INIT:
- return raw1394_iso_recv_init(fi, argp);
- default:
- return -EINVAL;
- }
-}
-
-static long raw1394_ioctl_recv(struct file_info *fi, unsigned int cmd,
- unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
-
- switch (cmd) {
- case RAW1394_IOC_ISO_RECV_START:{
- int args[3];
-
- if (copy_from_user(&args[0], argp, sizeof(args)))
- return -EFAULT;
- return hpsb_iso_recv_start(fi->iso_handle,
- args[0], args[1], args[2]);
- }
- case RAW1394_IOC_ISO_XMIT_RECV_STOP:
- hpsb_iso_stop(fi->iso_handle);
- return 0;
- case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL:
- return hpsb_iso_recv_listen_channel(fi->iso_handle, arg);
- case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL:
- return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg);
- case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK:{
- u64 mask;
-
- if (copy_from_user(&mask, argp, sizeof(mask)))
- return -EFAULT;
- return hpsb_iso_recv_set_channel_mask(fi->iso_handle,
- mask);
- }
- case RAW1394_IOC_ISO_GET_STATUS:
- return raw1394_iso_get_status(fi, argp);
- case RAW1394_IOC_ISO_RECV_PACKETS:
- return raw1394_iso_recv_packets(fi, argp);
- case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS:
- return hpsb_iso_recv_release_packets(fi->iso_handle, arg);
- case RAW1394_IOC_ISO_RECV_FLUSH:
- return hpsb_iso_recv_flush(fi->iso_handle);
- case RAW1394_IOC_ISO_SHUTDOWN:
- raw1394_iso_shutdown(fi);
- return 0;
- case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
- queue_rawiso_event(fi);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static long raw1394_ioctl_xmit(struct file_info *fi, unsigned int cmd,
- void __user *argp)
-{
- switch (cmd) {
- case RAW1394_IOC_ISO_XMIT_START:{
- int args[2];
-
- if (copy_from_user(&args[0], argp, sizeof(args)))
- return -EFAULT;
- return hpsb_iso_xmit_start(fi->iso_handle,
- args[0], args[1]);
- }
- case RAW1394_IOC_ISO_XMIT_SYNC:
- return hpsb_iso_xmit_sync(fi->iso_handle);
- case RAW1394_IOC_ISO_XMIT_RECV_STOP:
- hpsb_iso_stop(fi->iso_handle);
- return 0;
- case RAW1394_IOC_ISO_GET_STATUS:
- return raw1394_iso_get_status(fi, argp);
- case RAW1394_IOC_ISO_XMIT_PACKETS:
- return raw1394_iso_send_packets(fi, argp);
- case RAW1394_IOC_ISO_SHUTDOWN:
- raw1394_iso_shutdown(fi);
- return 0;
- case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
- queue_rawiso_event(fi);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/* ioctl is only used for rawiso operations */
-static long raw1394_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct file_info *fi = file->private_data;
- void __user *argp = (void __user *)arg;
- long ret;
-
- /* state-independent commands */
- switch(cmd) {
- case RAW1394_IOC_GET_CYCLE_TIMER:
- return raw1394_read_cycle_timer(fi, argp);
- default:
- break;
- }
-
- if (!mutex_trylock(&fi->state_mutex))
- return -EAGAIN;
-
- switch (fi->iso_state) {
- case RAW1394_ISO_INACTIVE:
- ret = raw1394_ioctl_inactive(fi, cmd, argp);
- break;
- case RAW1394_ISO_RECV:
- ret = raw1394_ioctl_recv(fi, cmd, arg);
- break;
- case RAW1394_ISO_XMIT:
- ret = raw1394_ioctl_xmit(fi, cmd, argp);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- mutex_unlock(&fi->state_mutex);
-
- return ret;
-}
-
-#ifdef CONFIG_COMPAT
-struct raw1394_iso_packets32 {
- __u32 n_packets;
- compat_uptr_t infos;
-} __attribute__((packed));
-
-struct raw1394_cycle_timer32 {
- __u32 cycle_timer;
- __u64 local_time;
-}
-#if defined(CONFIG_X86_64) || defined(CONFIG_IA64)
-__attribute__((packed))
-#endif
-;
-
-#define RAW1394_IOC_ISO_RECV_PACKETS32 \
- _IOW ('#', 0x25, struct raw1394_iso_packets32)
-#define RAW1394_IOC_ISO_XMIT_PACKETS32 \
- _IOW ('#', 0x27, struct raw1394_iso_packets32)
-#define RAW1394_IOC_GET_CYCLE_TIMER32 \
- _IOR ('#', 0x30, struct raw1394_cycle_timer32)
-
-static long raw1394_iso_xmit_recv_packets32(struct file *file, unsigned int cmd,
- struct raw1394_iso_packets32 __user *arg)
-{
- compat_uptr_t infos32;
- void __user *infos;
- long err = -EFAULT;
- struct raw1394_iso_packets __user *dst = compat_alloc_user_space(sizeof(struct raw1394_iso_packets));
-
- if (!copy_in_user(&dst->n_packets, &arg->n_packets, sizeof arg->n_packets) &&
- !copy_from_user(&infos32, &arg->infos, sizeof infos32)) {
- infos = compat_ptr(infos32);
- if (!copy_to_user(&dst->infos, &infos, sizeof infos))
- err = raw1394_ioctl(file, cmd, (unsigned long)dst);
- }
- return err;
-}
-
-static long raw1394_read_cycle_timer32(struct file_info *fi, void __user * uaddr)
-{
- struct raw1394_cycle_timer32 ct;
- int err;
-
- err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time);
- if (!err)
- if (copy_to_user(uaddr, &ct, sizeof(ct)))
- err = -EFAULT;
- return err;
-}
-
-static long raw1394_compat_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct file_info *fi = file->private_data;
- void __user *argp = (void __user *)arg;
- long err;
-
- switch (cmd) {
- /* These requests have same format as long as 'int' has same size. */
- case RAW1394_IOC_ISO_RECV_INIT:
- case RAW1394_IOC_ISO_RECV_START:
- case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL:
- case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL:
- case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK:
- case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS:
- case RAW1394_IOC_ISO_RECV_FLUSH:
- case RAW1394_IOC_ISO_XMIT_RECV_STOP:
- case RAW1394_IOC_ISO_XMIT_INIT:
- case RAW1394_IOC_ISO_XMIT_START:
- case RAW1394_IOC_ISO_XMIT_SYNC:
- case RAW1394_IOC_ISO_GET_STATUS:
- case RAW1394_IOC_ISO_SHUTDOWN:
- case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
- err = raw1394_ioctl(file, cmd, arg);
- break;
- /* These request have different format. */
- case RAW1394_IOC_ISO_RECV_PACKETS32:
- err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_RECV_PACKETS, argp);
- break;
- case RAW1394_IOC_ISO_XMIT_PACKETS32:
- err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_XMIT_PACKETS, argp);
- break;
- case RAW1394_IOC_GET_CYCLE_TIMER32:
- err = raw1394_read_cycle_timer32(fi, argp);
- break;
- default:
- err = -EINVAL;
- break;
- }
-
- return err;
-}
-#endif
-
-static unsigned int raw1394_poll(struct file *file, poll_table * pt)
-{
- struct file_info *fi = file->private_data;
- unsigned int mask = POLLOUT | POLLWRNORM;
- unsigned long flags;
-
- poll_wait(file, &fi->wait_complete, pt);
-
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- if (!list_empty(&fi->req_complete)) {
- mask |= POLLIN | POLLRDNORM;
- }
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-
- return mask;
-}
-
-static int raw1394_open(struct inode *inode, struct file *file)
-{
- struct file_info *fi;
-
- fi = kzalloc(sizeof(*fi), GFP_KERNEL);
- if (!fi)
- return -ENOMEM;
-
- fi->notification = (u8) RAW1394_NOTIFY_ON; /* busreset notification */
-
- INIT_LIST_HEAD(&fi->list);
- mutex_init(&fi->state_mutex);
- fi->state = opened;
- INIT_LIST_HEAD(&fi->req_pending);
- INIT_LIST_HEAD(&fi->req_complete);
- spin_lock_init(&fi->reqlists_lock);
- init_waitqueue_head(&fi->wait_complete);
- INIT_LIST_HEAD(&fi->addr_list);
-
- file->private_data = fi;
-
- return nonseekable_open(inode, file);
-}
-
-static int raw1394_release(struct inode *inode, struct file *file)
-{
- struct file_info *fi = file->private_data;
- struct list_head *lh;
- struct pending_request *req;
- int i, fail;
- int retval = 0;
- struct list_head *entry;
- struct arm_addr *addr = NULL;
- struct host_info *hi;
- struct file_info *fi_hlp = NULL;
- struct arm_addr *arm_addr = NULL;
- int another_host;
- int csr_mod = 0;
- unsigned long flags;
-
- if (fi->iso_state != RAW1394_ISO_INACTIVE)
- raw1394_iso_shutdown(fi);
-
- spin_lock_irqsave(&host_info_lock, flags);
-
- fail = 0;
- /* set address-entries invalid */
-
- while (!list_empty(&fi->addr_list)) {
- another_host = 0;
- lh = fi->addr_list.next;
- addr = list_entry(lh, struct arm_addr, addr_list);
- /* another host with valid address-entry containing
- same addressrange? */
- list_for_each_entry(hi, &host_info_list, list) {
- if (hi->host != fi->host) {
- list_for_each_entry(fi_hlp, &hi->file_info_list,
- list) {
- entry = fi_hlp->addr_list.next;
- while (entry != &(fi_hlp->addr_list)) {
- arm_addr = list_entry(entry, struct
- arm_addr,
- addr_list);
- if (arm_addr->start ==
- addr->start) {
- DBGMSG
- ("raw1394_release: "
- "another host ownes "
- "same addressrange");
- another_host = 1;
- break;
- }
- entry = entry->next;
- }
- if (another_host) {
- break;
- }
- }
- }
- }
- if (!another_host) {
- DBGMSG("raw1394_release: call hpsb_arm_unregister");
- retval =
- hpsb_unregister_addrspace(&raw1394_highlevel,
- fi->host, addr->start);
- if (!retval) {
- ++fail;
- printk(KERN_ERR
- "raw1394_release arm_Unregister failed\n");
- }
- }
- DBGMSG("raw1394_release: delete addr_entry from list");
- list_del(&addr->addr_list);
- vfree(addr->addr_space_buffer);
- kfree(addr);
- } /* while */
- spin_unlock_irqrestore(&host_info_lock, flags);
- if (fail > 0) {
- printk(KERN_ERR "raw1394: during addr_list-release "
- "error(s) occurred \n");
- }
-
- for (;;) {
- /* This locked section guarantees that neither
- * complete nor pending requests exist once i!=0 */
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- while ((req = __next_complete_req(fi)))
- free_pending_request(req);
-
- i = list_empty(&fi->req_pending);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-
- if (i)
- break;
- /*
- * Sleep until more requests can be freed.
- *
- * NB: We call the macro wait_event() with a condition argument
- * with side effect. This is only possible because the side
- * effect does not occur until the condition became true, and
- * wait_event() won't evaluate the condition again after that.
- */
- wait_event(fi->wait_complete, (req = next_complete_req(fi)));
- free_pending_request(req);
- }
-
- /* Remove any sub-trees left by user space programs */
- for (i = 0; i < RAW1394_MAX_USER_CSR_DIRS; i++) {
- struct csr1212_dentry *dentry;
- if (!fi->csr1212_dirs[i])
- continue;
- for (dentry =
- fi->csr1212_dirs[i]->value.directory.dentries_head; dentry;
- dentry = dentry->next) {
- csr1212_detach_keyval_from_directory(fi->host->csr.rom->
- root_kv,
- dentry->kv);
- }
- csr1212_release_keyval(fi->csr1212_dirs[i]);
- fi->csr1212_dirs[i] = NULL;
- csr_mod = 1;
- }
-
- if ((csr_mod || fi->cfgrom_upd)
- && hpsb_update_config_rom_image(fi->host) < 0)
- HPSB_ERR
- ("Failed to generate Configuration ROM image for host %d",
- fi->host->id);
-
- if (fi->state == connected) {
- spin_lock_irqsave(&host_info_lock, flags);
- list_del(&fi->list);
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- put_device(&fi->host->device);
- }
-
- spin_lock_irqsave(&host_info_lock, flags);
- if (fi->host)
- module_put(fi->host->driver->owner);
- spin_unlock_irqrestore(&host_info_lock, flags);
-
- kfree(fi);
-
- return 0;
-}
-
-/*** HOTPLUG STUFF **********************************************************/
-/*
- * Export information about protocols/devices supported by this driver.
- */
-#ifdef MODULE
-static const struct ieee1394_device_id raw1394_id_table[] = {
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = AVC_SW_VERSION_ENTRY & 0xffffff},
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = CAMERA_SW_VERSION_ENTRY & 0xffffff},
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff},
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff},
- {}
-};
-
-MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table);
-#endif /* MODULE */
-
-static struct hpsb_protocol_driver raw1394_driver = {
- .name = "raw1394",
-};
-
-/******************************************************************************/
-
-static struct hpsb_highlevel raw1394_highlevel = {
- .name = RAW1394_DEVICE_NAME,
- .add_host = add_host,
- .remove_host = remove_host,
- .host_reset = host_reset,
- .fcp_request = fcp_request,
-};
-
-static struct cdev raw1394_cdev;
-static const struct file_operations raw1394_fops = {
- .owner = THIS_MODULE,
- .read = raw1394_read,
- .write = raw1394_write,
- .mmap = raw1394_mmap,
- .unlocked_ioctl = raw1394_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = raw1394_compat_ioctl,
-#endif
- .poll = raw1394_poll,
- .open = raw1394_open,
- .release = raw1394_release,
- .llseek = no_llseek,
-};
-
-static int __init init_raw1394(void)
-{
- int ret = 0;
-
- hpsb_register_highlevel(&raw1394_highlevel);
-
- if (IS_ERR
- (device_create(hpsb_protocol_class, NULL,
- MKDEV(IEEE1394_MAJOR,
- IEEE1394_MINOR_BLOCK_RAW1394 * 16),
- NULL, RAW1394_DEVICE_NAME))) {
- ret = -EFAULT;
- goto out_unreg;
- }
-
- cdev_init(&raw1394_cdev, &raw1394_fops);
- raw1394_cdev.owner = THIS_MODULE;
- ret = cdev_add(&raw1394_cdev, IEEE1394_RAW1394_DEV, 1);
- if (ret) {
- HPSB_ERR("raw1394 failed to register minor device block");
- goto out_dev;
- }
-
- HPSB_INFO("raw1394: /dev/%s device initialized", RAW1394_DEVICE_NAME);
-
- ret = hpsb_register_protocol(&raw1394_driver);
- if (ret) {
- HPSB_ERR("raw1394: failed to register protocol");
- cdev_del(&raw1394_cdev);
- goto out_dev;
- }
-
- goto out;
-
- out_dev:
- device_destroy(hpsb_protocol_class,
- MKDEV(IEEE1394_MAJOR,
- IEEE1394_MINOR_BLOCK_RAW1394 * 16));
- out_unreg:
- hpsb_unregister_highlevel(&raw1394_highlevel);
- out:
- return ret;
-}
-
-static void __exit cleanup_raw1394(void)
-{
- device_destroy(hpsb_protocol_class,
- MKDEV(IEEE1394_MAJOR,
- IEEE1394_MINOR_BLOCK_RAW1394 * 16));
- cdev_del(&raw1394_cdev);
- hpsb_unregister_highlevel(&raw1394_highlevel);
- hpsb_unregister_protocol(&raw1394_driver);
-}
-
-module_init(init_raw1394);
-module_exit(cleanup_raw1394);
-MODULE_LICENSE("GPL");
diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h
deleted file mode 100644
index 963ac20373d2..000000000000
--- a/drivers/ieee1394/raw1394.h
+++ /dev/null
@@ -1,191 +0,0 @@
-#ifndef IEEE1394_RAW1394_H
-#define IEEE1394_RAW1394_H
-
-/* header for the raw1394 API that is exported to user-space */
-
-#define RAW1394_KERNELAPI_VERSION 4
-
-/* state: opened */
-#define RAW1394_REQ_INITIALIZE 1
-
-/* state: initialized */
-#define RAW1394_REQ_LIST_CARDS 2
-#define RAW1394_REQ_SET_CARD 3
-
-/* state: connected */
-#define RAW1394_REQ_ASYNC_READ 100
-#define RAW1394_REQ_ASYNC_WRITE 101
-#define RAW1394_REQ_LOCK 102
-#define RAW1394_REQ_LOCK64 103
-#define RAW1394_REQ_ISO_SEND 104 /* removed ABI, now a no-op */
-#define RAW1394_REQ_ASYNC_SEND 105
-#define RAW1394_REQ_ASYNC_STREAM 106
-
-#define RAW1394_REQ_ISO_LISTEN 200 /* removed ABI, now a no-op */
-#define RAW1394_REQ_FCP_LISTEN 201
-#define RAW1394_REQ_RESET_BUS 202
-#define RAW1394_REQ_GET_ROM 203
-#define RAW1394_REQ_UPDATE_ROM 204
-#define RAW1394_REQ_ECHO 205
-#define RAW1394_REQ_MODIFY_ROM 206
-
-#define RAW1394_REQ_ARM_REGISTER 300
-#define RAW1394_REQ_ARM_UNREGISTER 301
-#define RAW1394_REQ_ARM_SET_BUF 302
-#define RAW1394_REQ_ARM_GET_BUF 303
-
-#define RAW1394_REQ_RESET_NOTIFY 400
-
-#define RAW1394_REQ_PHYPACKET 500
-
-/* kernel to user */
-#define RAW1394_REQ_BUS_RESET 10000
-#define RAW1394_REQ_ISO_RECEIVE 10001
-#define RAW1394_REQ_FCP_REQUEST 10002
-#define RAW1394_REQ_ARM 10003
-#define RAW1394_REQ_RAWISO_ACTIVITY 10004
-
-/* error codes */
-#define RAW1394_ERROR_NONE 0
-#define RAW1394_ERROR_COMPAT (-1001)
-#define RAW1394_ERROR_STATE_ORDER (-1002)
-#define RAW1394_ERROR_GENERATION (-1003)
-#define RAW1394_ERROR_INVALID_ARG (-1004)
-#define RAW1394_ERROR_MEMFAULT (-1005)
-#define RAW1394_ERROR_ALREADY (-1006)
-
-#define RAW1394_ERROR_EXCESSIVE (-1020)
-#define RAW1394_ERROR_UNTIDY_LEN (-1021)
-
-#define RAW1394_ERROR_SEND_ERROR (-1100)
-#define RAW1394_ERROR_ABORTED (-1101)
-#define RAW1394_ERROR_TIMEOUT (-1102)
-
-/* arm_codes */
-#define ARM_READ 1
-#define ARM_WRITE 2
-#define ARM_LOCK 4
-
-#define RAW1394_LONG_RESET 0
-#define RAW1394_SHORT_RESET 1
-
-/* busresetnotify ... */
-#define RAW1394_NOTIFY_OFF 0
-#define RAW1394_NOTIFY_ON 1
-
-#include <asm/types.h>
-
-struct raw1394_request {
- __u32 type;
- __s32 error;
- __u32 misc;
-
- __u32 generation;
- __u32 length;
-
- __u64 address;
-
- __u64 tag;
-
- __u64 sendb;
- __u64 recvb;
-};
-
-struct raw1394_khost_list {
- __u32 nodes;
- __u8 name[32];
-};
-
-typedef struct arm_request {
- __u16 destination_nodeid;
- __u16 source_nodeid;
- __u64 destination_offset;
- __u8 tlabel;
- __u8 tcode;
- __u8 extended_transaction_code;
- __u32 generation;
- __u16 buffer_length;
- __u8 __user *buffer;
-} *arm_request_t;
-
-typedef struct arm_response {
- __s32 response_code;
- __u16 buffer_length;
- __u8 __user *buffer;
-} *arm_response_t;
-
-typedef struct arm_request_response {
- struct arm_request __user *request;
- struct arm_response __user *response;
-} *arm_request_response_t;
-
-/* rawiso API */
-#include "ieee1394-ioctl.h"
-
-/* per-packet metadata embedded in the ringbuffer */
-/* must be identical to hpsb_iso_packet_info in iso.h! */
-struct raw1394_iso_packet_info {
- __u32 offset;
- __u16 len;
- __u16 cycle; /* recv only */
- __u8 channel; /* recv only */
- __u8 tag;
- __u8 sy;
-};
-
-/* argument for RAW1394_ISO_RECV/XMIT_PACKETS ioctls */
-struct raw1394_iso_packets {
- __u32 n_packets;
- struct raw1394_iso_packet_info __user *infos;
-};
-
-struct raw1394_iso_config {
- /* size of packet data buffer, in bytes (will be rounded up to PAGE_SIZE) */
- __u32 data_buf_size;
-
- /* # of packets to buffer */
- __u32 buf_packets;
-
- /* iso channel (set to -1 for multi-channel recv) */
- __s32 channel;
-
- /* xmit only - iso transmission speed */
- __u8 speed;
-
- /* The mode of the dma when receiving iso data. Must be supported by chip */
- __u8 dma_mode;
-
- /* max. latency of buffer, in packets (-1 if you don't care) */
- __s32 irq_interval;
-};
-
-/* argument to RAW1394_ISO_XMIT/RECV_INIT and RAW1394_ISO_GET_STATUS */
-struct raw1394_iso_status {
- /* current settings */
- struct raw1394_iso_config config;
-
- /* number of packets waiting to be filled with data (ISO transmission)
- or containing data received (ISO reception) */
- __u32 n_packets;
-
- /* approximate number of packets dropped due to overflow or
- underflow of the packet buffer (a value of zero guarantees
- that no packets have been dropped) */
- __u32 overflows;
-
- /* cycle number at which next packet will be transmitted;
- -1 if not known */
- __s16 xmit_cycle;
-};
-
-/* argument to RAW1394_IOC_GET_CYCLE_TIMER ioctl */
-struct raw1394_cycle_timer {
- /* contents of Isochronous Cycle Timer register,
- as in OHCI 1.1 clause 5.13 (also with non-OHCI hosts) */
- __u32 cycle_timer;
-
- /* local time in microseconds since Epoch,
- simultaneously read with cycle timer */
- __u64 local_time;
-};
-#endif /* IEEE1394_RAW1394_H */
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
deleted file mode 100644
index d6e251a300ce..000000000000
--- a/drivers/ieee1394/sbp2.c
+++ /dev/null
@@ -1,2138 +0,0 @@
-/*
- * sbp2.c - SBP-2 protocol driver for IEEE-1394
- *
- * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com)
- * jamesg@filanet.com (JSG)
- *
- * Copyright (C) 2003 Ben Collins <bcollins@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- */
-
-/*
- * Brief Description:
- *
- * This driver implements the Serial Bus Protocol 2 (SBP-2) over IEEE-1394
- * under Linux. The SBP-2 driver is implemented as an IEEE-1394 high-level
- * driver. It also registers as a SCSI lower-level driver in order to accept
- * SCSI commands for transport using SBP-2.
- *
- * You may access any attached SBP-2 (usually storage devices) as regular
- * SCSI devices. E.g. mount /dev/sda1, fdisk, mkfs, etc..
- *
- * See http://www.t10.org/drafts.htm#sbp2 for the final draft of the SBP-2
- * specification and for where to purchase the official standard.
- *
- * TODO:
- * - look into possible improvements of the SCSI error handlers
- * - handle Unit_Characteristics.mgt_ORB_timeout and .ORB_size
- * - handle Logical_Unit_Number.ordered
- * - handle src == 1 in status blocks
- * - reimplement the DMA mapping in absence of physical DMA so that
- * bus_to_virt is no longer required
- * - debug the handling of absent physical DMA
- * - replace CONFIG_IEEE1394_SBP2_PHYS_DMA by automatic detection
- * (this is easy but depends on the previous two TODO items)
- * - make the parameter serialize_io configurable per device
- * - move all requests to fetch agent registers into non-atomic context,
- * replace all usages of sbp2util_node_write_no_wait by true transactions
- * Grep for inline FIXME comments below.
- */
-
-#include <linux/blkdev.h>
-#include <linux/compiler.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/stringify.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <linux/workqueue.h>
-#include <linux/scatterlist.h>
-
-#include <asm/byteorder.h>
-#include <asm/errno.h>
-#include <asm/param.h>
-#include <asm/system.h>
-#include <asm/types.h>
-
-#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
-#include <asm/io.h> /* for bus_to_virt */
-#endif
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-
-#include "csr1212.h"
-#include "highlevel.h"
-#include "hosts.h"
-#include "ieee1394.h"
-#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
-#include "ieee1394_transactions.h"
-#include "ieee1394_types.h"
-#include "nodemgr.h"
-#include "sbp2.h"
-
-/*
- * Module load parameter definitions
- */
-
-/*
- * Change max_speed on module load if you have a bad IEEE-1394
- * controller that has trouble running 2KB packets at 400mb.
- *
- * NOTE: On certain OHCI parts I have seen short packets on async transmit
- * (probably due to PCI latency/throughput issues with the part). You can
- * bump down the speed if you are running into problems.
- */
-static int sbp2_max_speed = IEEE1394_SPEED_MAX;
-module_param_named(max_speed, sbp2_max_speed, int, 0644);
-MODULE_PARM_DESC(max_speed, "Limit data transfer speed (5 <= 3200, "
- "4 <= 1600, 3 <= 800, 2 <= 400, 1 <= 200, 0 = 100 Mb/s)");
-
-/*
- * Set serialize_io to 0 or N to use dynamically appended lists of command ORBs.
- * This is and always has been buggy in multiple subtle ways. See above TODOs.
- */
-static int sbp2_serialize_io = 1;
-module_param_named(serialize_io, sbp2_serialize_io, bool, 0444);
-MODULE_PARM_DESC(serialize_io, "Serialize requests coming from SCSI drivers "
- "(default = Y, faster but buggy = N)");
-
-/*
- * Adjust max_sectors if you'd like to influence how many sectors each SCSI
- * command can transfer at most. Please note that some older SBP-2 bridge
- * chips are broken for transfers greater or equal to 128KB, therefore
- * max_sectors used to be a safe 255 sectors for many years. We now have a
- * default of 0 here which means that we let the SCSI stack choose a limit.
- *
- * The SBP2_WORKAROUND_128K_MAX_TRANS flag, if set either in the workarounds
- * module parameter or in the sbp2_workarounds_table[], will override the
- * value of max_sectors. We should use sbp2_workarounds_table[] to cover any
- * bridge chip which becomes known to need the 255 sectors limit.
- */
-static int sbp2_max_sectors;
-module_param_named(max_sectors, sbp2_max_sectors, int, 0444);
-MODULE_PARM_DESC(max_sectors, "Change max sectors per I/O supported "
- "(default = 0 = use SCSI stack's default)");
-
-/*
- * Exclusive login to sbp2 device? In most cases, the sbp2 driver should
- * do an exclusive login, as it's generally unsafe to have two hosts
- * talking to a single sbp2 device at the same time (filesystem coherency,
- * etc.). If you're running an sbp2 device that supports multiple logins,
- * and you're either running read-only filesystems or some sort of special
- * filesystem supporting multiple hosts, e.g. OpenGFS, Oracle Cluster
- * File System, or Lustre, then set exclusive_login to zero.
- *
- * So far only bridges from Oxford Semiconductor are known to support
- * concurrent logins. Depending on firmware, four or two concurrent logins
- * are possible on OXFW911 and newer Oxsemi bridges.
- */
-static int sbp2_exclusive_login = 1;
-module_param_named(exclusive_login, sbp2_exclusive_login, bool, 0644);
-MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
- "(default = Y, use N for concurrent initiators)");
-
-/*
- * If any of the following workarounds is required for your device to work,
- * please submit the kernel messages logged by sbp2 to the linux1394-devel
- * mailing list.
- *
- * - 128kB max transfer
- * Limit transfer size. Necessary for some old bridges.
- *
- * - 36 byte inquiry
- * When scsi_mod probes the device, let the inquiry command look like that
- * from MS Windows.
- *
- * - skip mode page 8
- * Suppress sending of mode_sense for mode page 8 if the device pretends to
- * support the SCSI Primary Block commands instead of Reduced Block Commands.
- *
- * - fix capacity
- * Tell sd_mod to correct the last sector number reported by read_capacity.
- * Avoids access beyond actual disk limits on devices with an off-by-one bug.
- * Don't use this with devices which don't have this bug.
- *
- * - delay inquiry
- * Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry.
- *
- * - power condition
- * Set the power condition field in the START STOP UNIT commands sent by
- * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on).
- * Some disks need this to spin down or to resume properly.
- *
- * - override internal blacklist
- * Instead of adding to the built-in blacklist, use only the workarounds
- * specified in the module load parameter.
- * Useful if a blacklist entry interfered with a non-broken device.
- */
-static int sbp2_default_workarounds;
-module_param_named(workarounds, sbp2_default_workarounds, int, 0644);
-MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
- ", 128kB max transfer = " __stringify(SBP2_WORKAROUND_128K_MAX_TRANS)
- ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36)
- ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
- ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
- ", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
- ", set power condition in start stop unit = "
- __stringify(SBP2_WORKAROUND_POWER_CONDITION)
- ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
- ", or a combination)");
-
-/*
- * This influences the format of the sysfs attribute
- * /sys/bus/scsi/devices/.../ieee1394_id.
- *
- * The default format is like in older kernels: %016Lx:%d:%d
- * It contains the target's EUI-64, a number given to the logical unit by
- * the ieee1394 driver's nodemgr (starting at 0), and the LUN.
- *
- * The long format is: %016Lx:%06x:%04x
- * It contains the target's EUI-64, the unit directory's directory_ID as per
- * IEEE 1212 clause 7.7.19, and the LUN. This format comes closest to the
- * format of SBP(-3) target port and logical unit identifier as per SAM (SCSI
- * Architecture Model) rev.2 to 4 annex A. Therefore and because it is
- * independent of the implementation of the ieee1394 nodemgr, the longer format
- * is recommended for future use.
- */
-static int sbp2_long_sysfs_ieee1394_id;
-module_param_named(long_ieee1394_id, sbp2_long_sysfs_ieee1394_id, bool, 0644);
-MODULE_PARM_DESC(long_ieee1394_id, "8+3+2 bytes format of ieee1394_id in sysfs "
- "(default = backwards-compatible = N, SAM-conforming = Y)");
-
-
-#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args)
-#define SBP2_ERR(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args)
-
-/*
- * Globals
- */
-static void sbp2scsi_complete_all_commands(struct sbp2_lu *, u32);
-static void sbp2scsi_complete_command(struct sbp2_lu *, u32, struct scsi_cmnd *,
- void (*)(struct scsi_cmnd *));
-static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *);
-static int sbp2_start_device(struct sbp2_lu *);
-static void sbp2_remove_device(struct sbp2_lu *);
-static int sbp2_login_device(struct sbp2_lu *);
-static int sbp2_reconnect_device(struct sbp2_lu *);
-static int sbp2_logout_device(struct sbp2_lu *);
-static void sbp2_host_reset(struct hpsb_host *);
-static int sbp2_handle_status_write(struct hpsb_host *, int, int, quadlet_t *,
- u64, size_t, u16);
-static int sbp2_agent_reset(struct sbp2_lu *, int);
-static void sbp2_parse_unit_directory(struct sbp2_lu *,
- struct unit_directory *);
-static int sbp2_set_busy_timeout(struct sbp2_lu *);
-static int sbp2_max_speed_and_size(struct sbp2_lu *);
-
-
-static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xa, 0xa, 0xa };
-
-static DEFINE_RWLOCK(sbp2_hi_logical_units_lock);
-
-static struct hpsb_highlevel sbp2_highlevel = {
- .name = SBP2_DEVICE_NAME,
- .host_reset = sbp2_host_reset,
-};
-
-static const struct hpsb_address_ops sbp2_ops = {
- .write = sbp2_handle_status_write
-};
-
-#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
-static int sbp2_handle_physdma_write(struct hpsb_host *, int, int, quadlet_t *,
- u64, size_t, u16);
-static int sbp2_handle_physdma_read(struct hpsb_host *, int, quadlet_t *, u64,
- size_t, u16);
-
-static const struct hpsb_address_ops sbp2_physdma_ops = {
- .read = sbp2_handle_physdma_read,
- .write = sbp2_handle_physdma_write,
-};
-#endif
-
-
-/*
- * Interface to driver core and IEEE 1394 core
- */
-static const struct ieee1394_device_id sbp2_id_table[] = {
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = SBP2_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = SBP2_SW_VERSION_ENTRY & 0xffffff},
- {}
-};
-MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table);
-
-static int sbp2_probe(struct device *);
-static int sbp2_remove(struct device *);
-static int sbp2_update(struct unit_directory *);
-
-static struct hpsb_protocol_driver sbp2_driver = {
- .name = SBP2_DEVICE_NAME,
- .id_table = sbp2_id_table,
- .update = sbp2_update,
- .driver = {
- .probe = sbp2_probe,
- .remove = sbp2_remove,
- },
-};
-
-
-/*
- * Interface to SCSI core
- */
-static int sbp2scsi_queuecommand(struct scsi_cmnd *,
- void (*)(struct scsi_cmnd *));
-static int sbp2scsi_abort(struct scsi_cmnd *);
-static int sbp2scsi_reset(struct scsi_cmnd *);
-static int sbp2scsi_slave_alloc(struct scsi_device *);
-static int sbp2scsi_slave_configure(struct scsi_device *);
-static void sbp2scsi_slave_destroy(struct scsi_device *);
-static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *,
- struct device_attribute *, char *);
-
-static DEVICE_ATTR(ieee1394_id, S_IRUGO, sbp2_sysfs_ieee1394_id_show, NULL);
-
-static struct device_attribute *sbp2_sysfs_sdev_attrs[] = {
- &dev_attr_ieee1394_id,
- NULL
-};
-
-static struct scsi_host_template sbp2_shost_template = {
- .module = THIS_MODULE,
- .name = "SBP-2 IEEE-1394",
- .proc_name = SBP2_DEVICE_NAME,
- .queuecommand = sbp2scsi_queuecommand,
- .eh_abort_handler = sbp2scsi_abort,
- .eh_device_reset_handler = sbp2scsi_reset,
- .slave_alloc = sbp2scsi_slave_alloc,
- .slave_configure = sbp2scsi_slave_configure,
- .slave_destroy = sbp2scsi_slave_destroy,
- .this_id = -1,
- .sg_tablesize = SG_ALL,
- .use_clustering = ENABLE_CLUSTERING,
- .cmd_per_lun = SBP2_MAX_CMDS,
- .can_queue = SBP2_MAX_CMDS,
- .sdev_attrs = sbp2_sysfs_sdev_attrs,
-};
-
-#define SBP2_ROM_VALUE_WILDCARD ~0 /* match all */
-#define SBP2_ROM_VALUE_MISSING 0xff000000 /* not present in the unit dir. */
-
-/*
- * List of devices with known bugs.
- *
- * The firmware_revision field, masked with 0xffff00, is the best indicator
- * for the type of bridge chip of a device. It yields a few false positives
- * but this did not break correctly behaving devices so far.
- */
-static const struct {
- u32 firmware_revision;
- u32 model;
- unsigned workarounds;
-} sbp2_workarounds_table[] = {
- /* DViCO Momobay CX-1 with TSB42AA9 bridge */ {
- .firmware_revision = 0x002800,
- .model = 0x001010,
- .workarounds = SBP2_WORKAROUND_INQUIRY_36 |
- SBP2_WORKAROUND_MODE_SENSE_8 |
- SBP2_WORKAROUND_POWER_CONDITION,
- },
- /* DViCO Momobay FX-3A with TSB42AA9A bridge */ {
- .firmware_revision = 0x002800,
- .model = 0x000000,
- .workarounds = SBP2_WORKAROUND_POWER_CONDITION,
- },
- /* Initio bridges, actually only needed for some older ones */ {
- .firmware_revision = 0x000200,
- .model = SBP2_ROM_VALUE_WILDCARD,
- .workarounds = SBP2_WORKAROUND_INQUIRY_36,
- },
- /* PL-3507 bridge with Prolific firmware */ {
- .firmware_revision = 0x012800,
- .model = SBP2_ROM_VALUE_WILDCARD,
- .workarounds = SBP2_WORKAROUND_POWER_CONDITION,
- },
- /* Symbios bridge */ {
- .firmware_revision = 0xa0b800,
- .model = SBP2_ROM_VALUE_WILDCARD,
- .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
- },
- /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ {
- .firmware_revision = 0x002600,
- .model = SBP2_ROM_VALUE_WILDCARD,
- .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
- },
- /*
- * iPod 2nd generation: needs 128k max transfer size workaround
- * iPod 3rd generation: needs fix capacity workaround
- */
- {
- .firmware_revision = 0x0a2700,
- .model = 0x000000,
- .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS |
- SBP2_WORKAROUND_FIX_CAPACITY,
- },
- /* iPod 4th generation */ {
- .firmware_revision = 0x0a2700,
- .model = 0x000021,
- .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
- },
- /* iPod mini */ {
- .firmware_revision = 0x0a2700,
- .model = 0x000022,
- .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
- },
- /* iPod mini */ {
- .firmware_revision = 0x0a2700,
- .model = 0x000023,
- .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
- },
- /* iPod Photo */ {
- .firmware_revision = 0x0a2700,
- .model = 0x00007e,
- .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
- }
-};
-
-/**************************************
- * General utility functions
- **************************************/
-
-#ifndef __BIG_ENDIAN
-/*
- * Converts a buffer from be32 to cpu byte ordering. Length is in bytes.
- */
-static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
-{
- u32 *temp = buffer;
-
- for (length = (length >> 2); length--; )
- temp[length] = be32_to_cpu(temp[length]);
-}
-
-/*
- * Converts a buffer from cpu to be32 byte ordering. Length is in bytes.
- */
-static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
-{
- u32 *temp = buffer;
-
- for (length = (length >> 2); length--; )
- temp[length] = cpu_to_be32(temp[length]);
-}
-#else /* BIG_ENDIAN */
-/* Why waste the cpu cycles? */
-#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0)
-#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0)
-#endif
-
-static DECLARE_WAIT_QUEUE_HEAD(sbp2_access_wq);
-
-/*
- * Waits for completion of an SBP-2 access request.
- * Returns nonzero if timed out or prematurely interrupted.
- */
-static int sbp2util_access_timeout(struct sbp2_lu *lu, int timeout)
-{
- long leftover;
-
- leftover = wait_event_interruptible_timeout(
- sbp2_access_wq, lu->access_complete, timeout);
- lu->access_complete = 0;
- return leftover <= 0;
-}
-
-static void sbp2_free_packet(void *packet)
-{
- hpsb_free_tlabel(packet);
- hpsb_free_packet(packet);
-}
-
-/*
- * This is much like hpsb_node_write(), except it ignores the response
- * subaction and returns immediately. Can be used from atomic context.
- */
-static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr,
- quadlet_t *buf, size_t len)
-{
- struct hpsb_packet *packet;
-
- packet = hpsb_make_writepacket(ne->host, ne->nodeid, addr, buf, len);
- if (!packet)
- return -ENOMEM;
-
- hpsb_set_packet_complete_task(packet, sbp2_free_packet, packet);
- hpsb_node_fill_packet(ne, packet);
- if (hpsb_send_packet(packet) < 0) {
- sbp2_free_packet(packet);
- return -EIO;
- }
- return 0;
-}
-
-static void sbp2util_notify_fetch_agent(struct sbp2_lu *lu, u64 offset,
- quadlet_t *data, size_t len)
-{
- /* There is a small window after a bus reset within which the node
- * entry's generation is current but the reconnect wasn't completed. */
- if (unlikely(atomic_read(&lu->state) == SBP2LU_STATE_IN_RESET))
- return;
-
- if (hpsb_node_write(lu->ne, lu->command_block_agent_addr + offset,
- data, len))
- SBP2_ERR("sbp2util_notify_fetch_agent failed.");
-
- /* Now accept new SCSI commands, unless a bus reset happended during
- * hpsb_node_write. */
- if (likely(atomic_read(&lu->state) != SBP2LU_STATE_IN_RESET))
- scsi_unblock_requests(lu->shost);
-}
-
-static void sbp2util_write_orb_pointer(struct work_struct *work)
-{
- struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work);
- quadlet_t data[2];
-
- data[0] = ORB_SET_NODE_ID(lu->hi->host->node_id);
- data[1] = lu->last_orb_dma;
- sbp2util_cpu_to_be32_buffer(data, 8);
- sbp2util_notify_fetch_agent(lu, SBP2_ORB_POINTER_OFFSET, data, 8);
-}
-
-static void sbp2util_write_doorbell(struct work_struct *work)
-{
- struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work);
-
- sbp2util_notify_fetch_agent(lu, SBP2_DOORBELL_OFFSET, NULL, 4);
-}
-
-static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu)
-{
- struct sbp2_command_info *cmd;
- struct device *dmadev = lu->hi->host->device.parent;
- int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS;
-
- for (i = 0; i < orbs; i++) {
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
- if (!cmd)
- goto failed_alloc;
-
- cmd->command_orb_dma =
- dma_map_single(dmadev, &cmd->command_orb,
- sizeof(struct sbp2_command_orb),
- DMA_TO_DEVICE);
- if (dma_mapping_error(dmadev, cmd->command_orb_dma))
- goto failed_orb;
-
- cmd->sge_dma =
- dma_map_single(dmadev, &cmd->scatter_gather_element,
- sizeof(cmd->scatter_gather_element),
- DMA_TO_DEVICE);
- if (dma_mapping_error(dmadev, cmd->sge_dma))
- goto failed_sge;
-
- INIT_LIST_HEAD(&cmd->list);
- list_add_tail(&cmd->list, &lu->cmd_orb_completed);
- }
- return 0;
-
-failed_sge:
- dma_unmap_single(dmadev, cmd->command_orb_dma,
- sizeof(struct sbp2_command_orb), DMA_TO_DEVICE);
-failed_orb:
- kfree(cmd);
-failed_alloc:
- return -ENOMEM;
-}
-
-static void sbp2util_remove_command_orb_pool(struct sbp2_lu *lu,
- struct hpsb_host *host)
-{
- struct list_head *lh, *next;
- struct sbp2_command_info *cmd;
- unsigned long flags;
-
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- if (!list_empty(&lu->cmd_orb_completed))
- list_for_each_safe(lh, next, &lu->cmd_orb_completed) {
- cmd = list_entry(lh, struct sbp2_command_info, list);
- dma_unmap_single(host->device.parent,
- cmd->command_orb_dma,
- sizeof(struct sbp2_command_orb),
- DMA_TO_DEVICE);
- dma_unmap_single(host->device.parent, cmd->sge_dma,
- sizeof(cmd->scatter_gather_element),
- DMA_TO_DEVICE);
- kfree(cmd);
- }
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
- return;
-}
-
-/*
- * Finds the sbp2_command for a given outstanding command ORB.
- * Only looks at the in-use list.
- */
-static struct sbp2_command_info *sbp2util_find_command_for_orb(
- struct sbp2_lu *lu, dma_addr_t orb)
-{
- struct sbp2_command_info *cmd;
- unsigned long flags;
-
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- if (!list_empty(&lu->cmd_orb_inuse))
- list_for_each_entry(cmd, &lu->cmd_orb_inuse, list)
- if (cmd->command_orb_dma == orb) {
- spin_unlock_irqrestore(
- &lu->cmd_orb_lock, flags);
- return cmd;
- }
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
- return NULL;
-}
-
-/*
- * Finds the sbp2_command for a given outstanding SCpnt.
- * Only looks at the in-use list.
- * Must be called with lu->cmd_orb_lock held.
- */
-static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(
- struct sbp2_lu *lu, void *SCpnt)
-{
- struct sbp2_command_info *cmd;
-
- if (!list_empty(&lu->cmd_orb_inuse))
- list_for_each_entry(cmd, &lu->cmd_orb_inuse, list)
- if (cmd->Current_SCpnt == SCpnt)
- return cmd;
- return NULL;
-}
-
-static struct sbp2_command_info *sbp2util_allocate_command_orb(
- struct sbp2_lu *lu,
- struct scsi_cmnd *Current_SCpnt,
- void (*Current_done)(struct scsi_cmnd *))
-{
- struct list_head *lh;
- struct sbp2_command_info *cmd = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- if (!list_empty(&lu->cmd_orb_completed)) {
- lh = lu->cmd_orb_completed.next;
- list_del(lh);
- cmd = list_entry(lh, struct sbp2_command_info, list);
- cmd->Current_done = Current_done;
- cmd->Current_SCpnt = Current_SCpnt;
- list_add_tail(&cmd->list, &lu->cmd_orb_inuse);
- } else
- SBP2_ERR("%s: no orbs available", __func__);
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
- return cmd;
-}
-
-/*
- * Unmaps the DMAs of a command and moves the command to the completed ORB list.
- * Must be called with lu->cmd_orb_lock held.
- */
-static void sbp2util_mark_command_completed(struct sbp2_lu *lu,
- struct sbp2_command_info *cmd)
-{
- if (scsi_sg_count(cmd->Current_SCpnt))
- dma_unmap_sg(lu->ud->ne->host->device.parent,
- scsi_sglist(cmd->Current_SCpnt),
- scsi_sg_count(cmd->Current_SCpnt),
- cmd->Current_SCpnt->sc_data_direction);
- list_move_tail(&cmd->list, &lu->cmd_orb_completed);
-}
-
-/*
- * Is lu valid? Is the 1394 node still present?
- */
-static inline int sbp2util_node_is_available(struct sbp2_lu *lu)
-{
- return lu && lu->ne && !lu->ne->in_limbo;
-}
-
-/*********************************************
- * IEEE-1394 core driver stack related section
- *********************************************/
-
-static int sbp2_probe(struct device *dev)
-{
- struct unit_directory *ud;
- struct sbp2_lu *lu;
-
- ud = container_of(dev, struct unit_directory, device);
-
- /* Don't probe UD's that have the LUN flag. We'll probe the LUN(s)
- * instead. */
- if (ud->flags & UNIT_DIRECTORY_HAS_LUN_DIRECTORY)
- return -ENODEV;
-
- lu = sbp2_alloc_device(ud);
- if (!lu)
- return -ENOMEM;
-
- sbp2_parse_unit_directory(lu, ud);
- return sbp2_start_device(lu);
-}
-
-static int sbp2_remove(struct device *dev)
-{
- struct unit_directory *ud;
- struct sbp2_lu *lu;
- struct scsi_device *sdev;
-
- ud = container_of(dev, struct unit_directory, device);
- lu = dev_get_drvdata(&ud->device);
- if (!lu)
- return 0;
-
- if (lu->shost) {
- /* Get rid of enqueued commands if there is no chance to
- * send them. */
- if (!sbp2util_node_is_available(lu))
- sbp2scsi_complete_all_commands(lu, DID_NO_CONNECT);
- /* scsi_remove_device() may trigger shutdown functions of SCSI
- * highlevel drivers which would deadlock if blocked. */
- atomic_set(&lu->state, SBP2LU_STATE_IN_SHUTDOWN);
- scsi_unblock_requests(lu->shost);
- }
- sdev = lu->sdev;
- if (sdev) {
- lu->sdev = NULL;
- scsi_remove_device(sdev);
- }
-
- sbp2_logout_device(lu);
- sbp2_remove_device(lu);
-
- return 0;
-}
-
-static int sbp2_update(struct unit_directory *ud)
-{
- struct sbp2_lu *lu = dev_get_drvdata(&ud->device);
-
- if (sbp2_reconnect_device(lu) != 0) {
- /*
- * Reconnect failed. If another bus reset happened,
- * let nodemgr proceed and call sbp2_update again later
- * (or sbp2_remove if this node went away).
- */
- if (!hpsb_node_entry_valid(lu->ne))
- return 0;
- /*
- * Or the target rejected the reconnect because we weren't
- * fast enough. Try a regular login, but first log out
- * just in case of any weirdness.
- */
- sbp2_logout_device(lu);
-
- if (sbp2_login_device(lu) != 0) {
- if (!hpsb_node_entry_valid(lu->ne))
- return 0;
-
- /* Maybe another initiator won the login. */
- SBP2_ERR("Failed to reconnect to sbp2 device!");
- return -EBUSY;
- }
- }
-
- sbp2_set_busy_timeout(lu);
- sbp2_agent_reset(lu, 1);
- sbp2_max_speed_and_size(lu);
-
- /* Complete any pending commands with busy (so they get retried)
- * and remove them from our queue. */
- sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY);
-
- /* Accept new commands unless there was another bus reset in the
- * meantime. */
- if (hpsb_node_entry_valid(lu->ne)) {
- atomic_set(&lu->state, SBP2LU_STATE_RUNNING);
- scsi_unblock_requests(lu->shost);
- }
- return 0;
-}
-
-static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud)
-{
- struct sbp2_fwhost_info *hi;
- struct Scsi_Host *shost = NULL;
- struct sbp2_lu *lu = NULL;
- unsigned long flags;
-
- lu = kzalloc(sizeof(*lu), GFP_KERNEL);
- if (!lu) {
- SBP2_ERR("failed to create lu");
- goto failed_alloc;
- }
-
- lu->ne = ud->ne;
- lu->ud = ud;
- lu->speed_code = IEEE1394_SPEED_100;
- lu->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100];
- lu->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE;
- INIT_LIST_HEAD(&lu->cmd_orb_inuse);
- INIT_LIST_HEAD(&lu->cmd_orb_completed);
- INIT_LIST_HEAD(&lu->lu_list);
- spin_lock_init(&lu->cmd_orb_lock);
- atomic_set(&lu->state, SBP2LU_STATE_RUNNING);
- INIT_WORK(&lu->protocol_work, NULL);
-
- dev_set_drvdata(&ud->device, lu);
-
- hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host);
- if (!hi) {
- hi = hpsb_create_hostinfo(&sbp2_highlevel, ud->ne->host,
- sizeof(*hi));
- if (!hi) {
- SBP2_ERR("failed to allocate hostinfo");
- goto failed_alloc;
- }
- hi->host = ud->ne->host;
- INIT_LIST_HEAD(&hi->logical_units);
-
-#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
- /* Handle data movement if physical dma is not
- * enabled or not supported on host controller */
- if (!hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host,
- &sbp2_physdma_ops,
- 0x0ULL, 0xfffffffcULL)) {
- SBP2_ERR("failed to register lower 4GB address range");
- goto failed_alloc;
- }
-#endif
- }
-
- if (dma_get_max_seg_size(hi->host->device.parent) > SBP2_MAX_SEG_SIZE)
- BUG_ON(dma_set_max_seg_size(hi->host->device.parent,
- SBP2_MAX_SEG_SIZE));
-
- /* Prevent unloading of the 1394 host */
- if (!try_module_get(hi->host->driver->owner)) {
- SBP2_ERR("failed to get a reference on 1394 host driver");
- goto failed_alloc;
- }
-
- lu->hi = hi;
-
- write_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
- list_add_tail(&lu->lu_list, &hi->logical_units);
- write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
-
- /* Register the status FIFO address range. We could use the same FIFO
- * for targets at different nodes. However we need different FIFOs per
- * target in order to support multi-unit devices.
- * The FIFO is located out of the local host controller's physical range
- * but, if possible, within the posted write area. Status writes will
- * then be performed as unified transactions. This slightly reduces
- * bandwidth usage, and some Prolific based devices seem to require it.
- */
- lu->status_fifo_addr = hpsb_allocate_and_register_addrspace(
- &sbp2_highlevel, ud->ne->host, &sbp2_ops,
- sizeof(struct sbp2_status_block), sizeof(quadlet_t),
- ud->ne->host->low_addr_space, CSR1212_ALL_SPACE_END);
- if (lu->status_fifo_addr == CSR1212_INVALID_ADDR_SPACE) {
- SBP2_ERR("failed to allocate status FIFO address range");
- goto failed_alloc;
- }
-
- shost = scsi_host_alloc(&sbp2_shost_template, sizeof(unsigned long));
- if (!shost) {
- SBP2_ERR("failed to register scsi host");
- goto failed_alloc;
- }
-
- shost->hostdata[0] = (unsigned long)lu;
- shost->max_cmd_len = SBP2_MAX_CDB_SIZE;
-
- if (!scsi_add_host(shost, &ud->device)) {
- lu->shost = shost;
- return lu;
- }
-
- SBP2_ERR("failed to add scsi host");
- scsi_host_put(shost);
-
-failed_alloc:
- sbp2_remove_device(lu);
- return NULL;
-}
-
-static void sbp2_host_reset(struct hpsb_host *host)
-{
- struct sbp2_fwhost_info *hi;
- struct sbp2_lu *lu;
- unsigned long flags;
-
- hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
- if (!hi)
- return;
-
- read_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
-
- list_for_each_entry(lu, &hi->logical_units, lu_list)
- if (atomic_cmpxchg(&lu->state,
- SBP2LU_STATE_RUNNING, SBP2LU_STATE_IN_RESET)
- == SBP2LU_STATE_RUNNING)
- scsi_block_requests(lu->shost);
-
- read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
-}
-
-static int sbp2_start_device(struct sbp2_lu *lu)
-{
- struct sbp2_fwhost_info *hi = lu->hi;
- int error;
-
- lu->login_response = dma_alloc_coherent(hi->host->device.parent,
- sizeof(struct sbp2_login_response),
- &lu->login_response_dma, GFP_KERNEL);
- if (!lu->login_response)
- goto alloc_fail;
-
- lu->query_logins_orb = dma_alloc_coherent(hi->host->device.parent,
- sizeof(struct sbp2_query_logins_orb),
- &lu->query_logins_orb_dma, GFP_KERNEL);
- if (!lu->query_logins_orb)
- goto alloc_fail;
-
- lu->query_logins_response = dma_alloc_coherent(hi->host->device.parent,
- sizeof(struct sbp2_query_logins_response),
- &lu->query_logins_response_dma, GFP_KERNEL);
- if (!lu->query_logins_response)
- goto alloc_fail;
-
- lu->reconnect_orb = dma_alloc_coherent(hi->host->device.parent,
- sizeof(struct sbp2_reconnect_orb),
- &lu->reconnect_orb_dma, GFP_KERNEL);
- if (!lu->reconnect_orb)
- goto alloc_fail;
-
- lu->logout_orb = dma_alloc_coherent(hi->host->device.parent,
- sizeof(struct sbp2_logout_orb),
- &lu->logout_orb_dma, GFP_KERNEL);
- if (!lu->logout_orb)
- goto alloc_fail;
-
- lu->login_orb = dma_alloc_coherent(hi->host->device.parent,
- sizeof(struct sbp2_login_orb),
- &lu->login_orb_dma, GFP_KERNEL);
- if (!lu->login_orb)
- goto alloc_fail;
-
- if (sbp2util_create_command_orb_pool(lu))
- goto alloc_fail;
-
- /* Wait a second before trying to log in. Previously logged in
- * initiators need a chance to reconnect. */
- if (msleep_interruptible(1000)) {
- sbp2_remove_device(lu);
- return -EINTR;
- }
-
- if (sbp2_login_device(lu)) {
- sbp2_remove_device(lu);
- return -EBUSY;
- }
-
- sbp2_set_busy_timeout(lu);
- sbp2_agent_reset(lu, 1);
- sbp2_max_speed_and_size(lu);
-
- if (lu->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY)
- ssleep(SBP2_INQUIRY_DELAY);
-
- error = scsi_add_device(lu->shost, 0, lu->ud->id, 0);
- if (error) {
- SBP2_ERR("scsi_add_device failed");
- sbp2_logout_device(lu);
- sbp2_remove_device(lu);
- return error;
- }
-
- return 0;
-
-alloc_fail:
- SBP2_ERR("Could not allocate memory for lu");
- sbp2_remove_device(lu);
- return -ENOMEM;
-}
-
-static void sbp2_remove_device(struct sbp2_lu *lu)
-{
- struct sbp2_fwhost_info *hi;
- unsigned long flags;
-
- if (!lu)
- return;
- hi = lu->hi;
- if (!hi)
- goto no_hi;
-
- if (lu->shost) {
- scsi_remove_host(lu->shost);
- scsi_host_put(lu->shost);
- }
- flush_scheduled_work();
- sbp2util_remove_command_orb_pool(lu, hi->host);
-
- write_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
- list_del(&lu->lu_list);
- write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
-
- if (lu->login_response)
- dma_free_coherent(hi->host->device.parent,
- sizeof(struct sbp2_login_response),
- lu->login_response,
- lu->login_response_dma);
- if (lu->login_orb)
- dma_free_coherent(hi->host->device.parent,
- sizeof(struct sbp2_login_orb),
- lu->login_orb,
- lu->login_orb_dma);
- if (lu->reconnect_orb)
- dma_free_coherent(hi->host->device.parent,
- sizeof(struct sbp2_reconnect_orb),
- lu->reconnect_orb,
- lu->reconnect_orb_dma);
- if (lu->logout_orb)
- dma_free_coherent(hi->host->device.parent,
- sizeof(struct sbp2_logout_orb),
- lu->logout_orb,
- lu->logout_orb_dma);
- if (lu->query_logins_orb)
- dma_free_coherent(hi->host->device.parent,
- sizeof(struct sbp2_query_logins_orb),
- lu->query_logins_orb,
- lu->query_logins_orb_dma);
- if (lu->query_logins_response)
- dma_free_coherent(hi->host->device.parent,
- sizeof(struct sbp2_query_logins_response),
- lu->query_logins_response,
- lu->query_logins_response_dma);
-
- if (lu->status_fifo_addr != CSR1212_INVALID_ADDR_SPACE)
- hpsb_unregister_addrspace(&sbp2_highlevel, hi->host,
- lu->status_fifo_addr);
-
- dev_set_drvdata(&lu->ud->device, NULL);
-
- module_put(hi->host->driver->owner);
-no_hi:
- kfree(lu);
-}
-
-#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
-/*
- * Deal with write requests on adapters which do not support physical DMA or
- * have it switched off.
- */
-static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid,
- int destid, quadlet_t *data, u64 addr,
- size_t length, u16 flags)
-{
- memcpy(bus_to_virt((u32) addr), data, length);
- return RCODE_COMPLETE;
-}
-
-/*
- * Deal with read requests on adapters which do not support physical DMA or
- * have it switched off.
- */
-static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid,
- quadlet_t *data, u64 addr, size_t length,
- u16 flags)
-{
- memcpy(data, bus_to_virt((u32) addr), length);
- return RCODE_COMPLETE;
-}
-#endif
-
-/**************************************
- * SBP-2 protocol related section
- **************************************/
-
-static int sbp2_query_logins(struct sbp2_lu *lu)
-{
- struct sbp2_fwhost_info *hi = lu->hi;
- quadlet_t data[2];
- int max_logins;
- int active_logins;
-
- lu->query_logins_orb->reserved1 = 0x0;
- lu->query_logins_orb->reserved2 = 0x0;
-
- lu->query_logins_orb->query_response_lo = lu->query_logins_response_dma;
- lu->query_logins_orb->query_response_hi =
- ORB_SET_NODE_ID(hi->host->node_id);
- lu->query_logins_orb->lun_misc =
- ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST);
- lu->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1);
- lu->query_logins_orb->lun_misc |= ORB_SET_LUN(lu->lun);
-
- lu->query_logins_orb->reserved_resp_length =
- ORB_SET_QUERY_LOGINS_RESP_LENGTH(
- sizeof(struct sbp2_query_logins_response));
-
- lu->query_logins_orb->status_fifo_hi =
- ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
- lu->query_logins_orb->status_fifo_lo =
- ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
-
- sbp2util_cpu_to_be32_buffer(lu->query_logins_orb,
- sizeof(struct sbp2_query_logins_orb));
-
- memset(lu->query_logins_response, 0,
- sizeof(struct sbp2_query_logins_response));
-
- data[0] = ORB_SET_NODE_ID(hi->host->node_id);
- data[1] = lu->query_logins_orb_dma;
- sbp2util_cpu_to_be32_buffer(data, 8);
-
- hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
-
- if (sbp2util_access_timeout(lu, 2*HZ)) {
- SBP2_INFO("Error querying logins to SBP-2 device - timed out");
- return -EIO;
- }
-
- if (lu->status_block.ORB_offset_lo != lu->query_logins_orb_dma) {
- SBP2_INFO("Error querying logins to SBP-2 device - timed out");
- return -EIO;
- }
-
- if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) {
- SBP2_INFO("Error querying logins to SBP-2 device - failed");
- return -EIO;
- }
-
- sbp2util_cpu_to_be32_buffer(lu->query_logins_response,
- sizeof(struct sbp2_query_logins_response));
-
- max_logins = RESPONSE_GET_MAX_LOGINS(
- lu->query_logins_response->length_max_logins);
- SBP2_INFO("Maximum concurrent logins supported: %d", max_logins);
-
- active_logins = RESPONSE_GET_ACTIVE_LOGINS(
- lu->query_logins_response->length_max_logins);
- SBP2_INFO("Number of active logins: %d", active_logins);
-
- if (active_logins >= max_logins) {
- return -EIO;
- }
-
- return 0;
-}
-
-static int sbp2_login_device(struct sbp2_lu *lu)
-{
- struct sbp2_fwhost_info *hi = lu->hi;
- quadlet_t data[2];
-
- if (!lu->login_orb)
- return -EIO;
-
- if (!sbp2_exclusive_login && sbp2_query_logins(lu)) {
- SBP2_INFO("Device does not support any more concurrent logins");
- return -EIO;
- }
-
- /* assume no password */
- lu->login_orb->password_hi = 0;
- lu->login_orb->password_lo = 0;
-
- lu->login_orb->login_response_lo = lu->login_response_dma;
- lu->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id);
- lu->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST);
-
- /* one second reconnect time */
- lu->login_orb->lun_misc |= ORB_SET_RECONNECT(0);
- lu->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(sbp2_exclusive_login);
- lu->login_orb->lun_misc |= ORB_SET_NOTIFY(1);
- lu->login_orb->lun_misc |= ORB_SET_LUN(lu->lun);
-
- lu->login_orb->passwd_resp_lengths =
- ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response));
-
- lu->login_orb->status_fifo_hi =
- ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
- lu->login_orb->status_fifo_lo =
- ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
-
- sbp2util_cpu_to_be32_buffer(lu->login_orb,
- sizeof(struct sbp2_login_orb));
-
- memset(lu->login_response, 0, sizeof(struct sbp2_login_response));
-
- data[0] = ORB_SET_NODE_ID(hi->host->node_id);
- data[1] = lu->login_orb_dma;
- sbp2util_cpu_to_be32_buffer(data, 8);
-
- hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
-
- /* wait up to 20 seconds for login status */
- if (sbp2util_access_timeout(lu, 20*HZ)) {
- SBP2_ERR("Error logging into SBP-2 device - timed out");
- return -EIO;
- }
-
- /* make sure that the returned status matches the login ORB */
- if (lu->status_block.ORB_offset_lo != lu->login_orb_dma) {
- SBP2_ERR("Error logging into SBP-2 device - timed out");
- return -EIO;
- }
-
- if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) {
- SBP2_ERR("Error logging into SBP-2 device - failed");
- return -EIO;
- }
-
- sbp2util_cpu_to_be32_buffer(lu->login_response,
- sizeof(struct sbp2_login_response));
- lu->command_block_agent_addr =
- ((u64)lu->login_response->command_block_agent_hi) << 32;
- lu->command_block_agent_addr |=
- ((u64)lu->login_response->command_block_agent_lo);
- lu->command_block_agent_addr &= 0x0000ffffffffffffULL;
-
- SBP2_INFO("Logged into SBP-2 device");
- return 0;
-}
-
-static int sbp2_logout_device(struct sbp2_lu *lu)
-{
- struct sbp2_fwhost_info *hi = lu->hi;
- quadlet_t data[2];
- int error;
-
- lu->logout_orb->reserved1 = 0x0;
- lu->logout_orb->reserved2 = 0x0;
- lu->logout_orb->reserved3 = 0x0;
- lu->logout_orb->reserved4 = 0x0;
-
- lu->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST);
- lu->logout_orb->login_ID_misc |=
- ORB_SET_LOGIN_ID(lu->login_response->length_login_ID);
- lu->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1);
-
- lu->logout_orb->reserved5 = 0x0;
- lu->logout_orb->status_fifo_hi =
- ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
- lu->logout_orb->status_fifo_lo =
- ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
-
- sbp2util_cpu_to_be32_buffer(lu->logout_orb,
- sizeof(struct sbp2_logout_orb));
-
- data[0] = ORB_SET_NODE_ID(hi->host->node_id);
- data[1] = lu->logout_orb_dma;
- sbp2util_cpu_to_be32_buffer(data, 8);
-
- error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
- if (error)
- return error;
-
- /* wait up to 1 second for the device to complete logout */
- if (sbp2util_access_timeout(lu, HZ))
- return -EIO;
-
- SBP2_INFO("Logged out of SBP-2 device");
- return 0;
-}
-
-static int sbp2_reconnect_device(struct sbp2_lu *lu)
-{
- struct sbp2_fwhost_info *hi = lu->hi;
- quadlet_t data[2];
- int error;
-
- lu->reconnect_orb->reserved1 = 0x0;
- lu->reconnect_orb->reserved2 = 0x0;
- lu->reconnect_orb->reserved3 = 0x0;
- lu->reconnect_orb->reserved4 = 0x0;
-
- lu->reconnect_orb->login_ID_misc =
- ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST);
- lu->reconnect_orb->login_ID_misc |=
- ORB_SET_LOGIN_ID(lu->login_response->length_login_ID);
- lu->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1);
-
- lu->reconnect_orb->reserved5 = 0x0;
- lu->reconnect_orb->status_fifo_hi =
- ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
- lu->reconnect_orb->status_fifo_lo =
- ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
-
- sbp2util_cpu_to_be32_buffer(lu->reconnect_orb,
- sizeof(struct sbp2_reconnect_orb));
-
- data[0] = ORB_SET_NODE_ID(hi->host->node_id);
- data[1] = lu->reconnect_orb_dma;
- sbp2util_cpu_to_be32_buffer(data, 8);
-
- error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
- if (error)
- return error;
-
- /* wait up to 1 second for reconnect status */
- if (sbp2util_access_timeout(lu, HZ)) {
- SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
- return -EIO;
- }
-
- /* make sure that the returned status matches the reconnect ORB */
- if (lu->status_block.ORB_offset_lo != lu->reconnect_orb_dma) {
- SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
- return -EIO;
- }
-
- if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) {
- SBP2_ERR("Error reconnecting to SBP-2 device - failed");
- return -EIO;
- }
-
- SBP2_INFO("Reconnected to SBP-2 device");
- return 0;
-}
-
-/*
- * Set the target node's Single Phase Retry limit. Affects the target's retry
- * behaviour if our node is too busy to accept requests.
- */
-static int sbp2_set_busy_timeout(struct sbp2_lu *lu)
-{
- quadlet_t data;
-
- data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE);
- if (hpsb_node_write(lu->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4))
- SBP2_ERR("%s error", __func__);
- return 0;
-}
-
-static void sbp2_parse_unit_directory(struct sbp2_lu *lu,
- struct unit_directory *ud)
-{
- struct csr1212_keyval *kv;
- struct csr1212_dentry *dentry;
- u64 management_agent_addr;
- u32 firmware_revision, model;
- unsigned workarounds;
- int i;
-
- management_agent_addr = 0;
- firmware_revision = SBP2_ROM_VALUE_MISSING;
- model = ud->flags & UNIT_DIRECTORY_MODEL_ID ?
- ud->model_id : SBP2_ROM_VALUE_MISSING;
-
- csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) {
- switch (kv->key.id) {
- case CSR1212_KV_ID_DEPENDENT_INFO:
- if (kv->key.type == CSR1212_KV_TYPE_CSR_OFFSET)
- management_agent_addr =
- CSR1212_REGISTER_SPACE_BASE +
- (kv->value.csr_offset << 2);
-
- else if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE)
- lu->lun = ORB_SET_LUN(kv->value.immediate);
- break;
-
-
- case SBP2_FIRMWARE_REVISION_KEY:
- firmware_revision = kv->value.immediate;
- break;
-
- default:
- /* FIXME: Check for SBP2_UNIT_CHARACTERISTICS_KEY
- * mgt_ORB_timeout and ORB_size, SBP-2 clause 7.4.8. */
-
- /* FIXME: Check for SBP2_DEVICE_TYPE_AND_LUN_KEY.
- * Its "ordered" bit has consequences for command ORB
- * list handling. See SBP-2 clauses 4.6, 7.4.11, 10.2 */
- break;
- }
- }
-
- workarounds = sbp2_default_workarounds;
-
- if (!(workarounds & SBP2_WORKAROUND_OVERRIDE))
- for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) {
- if (sbp2_workarounds_table[i].firmware_revision !=
- SBP2_ROM_VALUE_WILDCARD &&
- sbp2_workarounds_table[i].firmware_revision !=
- (firmware_revision & 0xffff00))
- continue;
- if (sbp2_workarounds_table[i].model !=
- SBP2_ROM_VALUE_WILDCARD &&
- sbp2_workarounds_table[i].model != model)
- continue;
- workarounds |= sbp2_workarounds_table[i].workarounds;
- break;
- }
-
- if (workarounds)
- SBP2_INFO("Workarounds for node " NODE_BUS_FMT ": 0x%x "
- "(firmware_revision 0x%06x, vendor_id 0x%06x,"
- " model_id 0x%06x)",
- NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid),
- workarounds, firmware_revision, ud->vendor_id,
- model);
-
- /* We would need one SCSI host template for each target to adjust
- * max_sectors on the fly, therefore warn only. */
- if (workarounds & SBP2_WORKAROUND_128K_MAX_TRANS &&
- (sbp2_max_sectors * 512) > (128 * 1024))
- SBP2_INFO("Node " NODE_BUS_FMT ": Bridge only supports 128KB "
- "max transfer size. WARNING: Current max_sectors "
- "setting is larger than 128KB (%d sectors)",
- NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid),
- sbp2_max_sectors);
-
- /* If this is a logical unit directory entry, process the parent
- * to get the values. */
- if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) {
- struct unit_directory *parent_ud = container_of(
- ud->device.parent, struct unit_directory, device);
- sbp2_parse_unit_directory(lu, parent_ud);
- } else {
- lu->management_agent_addr = management_agent_addr;
- lu->workarounds = workarounds;
- if (ud->flags & UNIT_DIRECTORY_HAS_LUN)
- lu->lun = ORB_SET_LUN(ud->lun);
- }
-}
-
-#define SBP2_PAYLOAD_TO_BYTES(p) (1 << ((p) + 2))
-
-/*
- * This function is called in order to determine the max speed and packet
- * size we can use in our ORBs. Note, that we (the driver and host) only
- * initiate the transaction. The SBP-2 device actually transfers the data
- * (by reading from the DMA area we tell it). This means that the SBP-2
- * device decides the actual maximum data it can transfer. We just tell it
- * the speed that it needs to use, and the max_rec the host supports, and
- * it takes care of the rest.
- */
-static int sbp2_max_speed_and_size(struct sbp2_lu *lu)
-{
- struct sbp2_fwhost_info *hi = lu->hi;
- u8 payload;
-
- lu->speed_code = hi->host->speed[NODEID_TO_NODE(lu->ne->nodeid)];
-
- if (lu->speed_code > sbp2_max_speed) {
- lu->speed_code = sbp2_max_speed;
- SBP2_INFO("Reducing speed to %s",
- hpsb_speedto_str[sbp2_max_speed]);
- }
-
- /* Payload size is the lesser of what our speed supports and what
- * our host supports. */
- payload = min(sbp2_speedto_max_payload[lu->speed_code],
- (u8) (hi->host->csr.max_rec - 1));
-
- /* If physical DMA is off, work around limitation in ohci1394:
- * packet size must not exceed PAGE_SIZE */
- if (lu->ne->host->low_addr_space < (1ULL << 32))
- while (SBP2_PAYLOAD_TO_BYTES(payload) + 24 > PAGE_SIZE &&
- payload)
- payload--;
-
- SBP2_INFO("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]",
- NODE_BUS_ARGS(hi->host, lu->ne->nodeid),
- hpsb_speedto_str[lu->speed_code],
- SBP2_PAYLOAD_TO_BYTES(payload));
-
- lu->max_payload_size = payload;
- return 0;
-}
-
-static int sbp2_agent_reset(struct sbp2_lu *lu, int wait)
-{
- quadlet_t data;
- u64 addr;
- int retval;
- unsigned long flags;
-
- /* flush lu->protocol_work */
- if (wait)
- flush_scheduled_work();
-
- data = ntohl(SBP2_AGENT_RESET_DATA);
- addr = lu->command_block_agent_addr + SBP2_AGENT_RESET_OFFSET;
-
- if (wait)
- retval = hpsb_node_write(lu->ne, addr, &data, 4);
- else
- retval = sbp2util_node_write_no_wait(lu->ne, addr, &data, 4);
-
- if (retval < 0) {
- SBP2_ERR("hpsb_node_write failed.\n");
- return -EIO;
- }
-
- /* make sure that the ORB_POINTER is written on next command */
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- lu->last_orb = NULL;
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
-
- return 0;
-}
-
-static int sbp2_prep_command_orb_sg(struct sbp2_command_orb *orb,
- struct sbp2_fwhost_info *hi,
- struct sbp2_command_info *cmd,
- unsigned int sg_count,
- struct scatterlist *sg,
- u32 orb_direction,
- enum dma_data_direction dma_dir)
-{
- struct device *dmadev = hi->host->device.parent;
- struct sbp2_unrestricted_page_table *pt;
- int i, n;
-
- n = dma_map_sg(dmadev, sg, sg_count, dma_dir);
- if (n == 0)
- return -ENOMEM;
-
- orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
- orb->misc |= ORB_SET_DIRECTION(orb_direction);
-
- /* special case if only one element (and less than 64KB in size) */
- if (n == 1) {
- orb->misc |= ORB_SET_DATA_SIZE(sg_dma_len(sg));
- orb->data_descriptor_lo = sg_dma_address(sg);
- } else {
- pt = &cmd->scatter_gather_element[0];
-
- dma_sync_single_for_cpu(dmadev, cmd->sge_dma,
- sizeof(cmd->scatter_gather_element),
- DMA_TO_DEVICE);
-
- for_each_sg(sg, sg, n, i) {
- pt[i].high = cpu_to_be32(sg_dma_len(sg) << 16);
- pt[i].low = cpu_to_be32(sg_dma_address(sg));
- }
-
- orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1) |
- ORB_SET_DATA_SIZE(n);
- orb->data_descriptor_lo = cmd->sge_dma;
-
- dma_sync_single_for_device(dmadev, cmd->sge_dma,
- sizeof(cmd->scatter_gather_element),
- DMA_TO_DEVICE);
- }
- return 0;
-}
-
-static int sbp2_create_command_orb(struct sbp2_lu *lu,
- struct sbp2_command_info *cmd,
- struct scsi_cmnd *SCpnt)
-{
- struct device *dmadev = lu->hi->host->device.parent;
- struct sbp2_command_orb *orb = &cmd->command_orb;
- unsigned int scsi_request_bufflen = scsi_bufflen(SCpnt);
- enum dma_data_direction dma_dir = SCpnt->sc_data_direction;
- u32 orb_direction;
- int ret;
-
- dma_sync_single_for_cpu(dmadev, cmd->command_orb_dma,
- sizeof(struct sbp2_command_orb), DMA_TO_DEVICE);
- /*
- * Set-up our command ORB.
- *
- * NOTE: We're doing unrestricted page tables (s/g), as this is
- * best performance (at least with the devices I have). This means
- * that data_size becomes the number of s/g elements, and
- * page_size should be zero (for unrestricted).
- */
- orb->next_ORB_hi = ORB_SET_NULL_PTR(1);
- orb->next_ORB_lo = 0x0;
- orb->misc = ORB_SET_MAX_PAYLOAD(lu->max_payload_size);
- orb->misc |= ORB_SET_SPEED(lu->speed_code);
- orb->misc |= ORB_SET_NOTIFY(1);
-
- if (dma_dir == DMA_NONE)
- orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
- else if (dma_dir == DMA_TO_DEVICE && scsi_request_bufflen)
- orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA;
- else if (dma_dir == DMA_FROM_DEVICE && scsi_request_bufflen)
- orb_direction = ORB_DIRECTION_READ_FROM_MEDIA;
- else {
- SBP2_INFO("Falling back to DMA_NONE");
- orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
- }
-
- /* set up our page table stuff */
- if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) {
- orb->data_descriptor_hi = 0x0;
- orb->data_descriptor_lo = 0x0;
- orb->misc |= ORB_SET_DIRECTION(1);
- ret = 0;
- } else {
- ret = sbp2_prep_command_orb_sg(orb, lu->hi, cmd,
- scsi_sg_count(SCpnt),
- scsi_sglist(SCpnt),
- orb_direction, dma_dir);
- }
- sbp2util_cpu_to_be32_buffer(orb, sizeof(*orb));
-
- memset(orb->cdb, 0, sizeof(orb->cdb));
- memcpy(orb->cdb, SCpnt->cmnd, SCpnt->cmd_len);
-
- dma_sync_single_for_device(dmadev, cmd->command_orb_dma,
- sizeof(struct sbp2_command_orb), DMA_TO_DEVICE);
- return ret;
-}
-
-static void sbp2_link_orb_command(struct sbp2_lu *lu,
- struct sbp2_command_info *cmd)
-{
- struct sbp2_fwhost_info *hi = lu->hi;
- struct sbp2_command_orb *last_orb;
- dma_addr_t last_orb_dma;
- u64 addr = lu->command_block_agent_addr;
- quadlet_t data[2];
- size_t length;
- unsigned long flags;
-
- /* check to see if there are any previous orbs to use */
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- last_orb = lu->last_orb;
- last_orb_dma = lu->last_orb_dma;
- if (!last_orb) {
- /*
- * last_orb == NULL means: We know that the target's fetch agent
- * is not active right now.
- */
- addr += SBP2_ORB_POINTER_OFFSET;
- data[0] = ORB_SET_NODE_ID(hi->host->node_id);
- data[1] = cmd->command_orb_dma;
- sbp2util_cpu_to_be32_buffer(data, 8);
- length = 8;
- } else {
- /*
- * last_orb != NULL means: We know that the target's fetch agent
- * is (very probably) not dead or in reset state right now.
- * We have an ORB already sent that we can append a new one to.
- * The target's fetch agent may or may not have read this
- * previous ORB yet.
- */
- dma_sync_single_for_cpu(hi->host->device.parent, last_orb_dma,
- sizeof(struct sbp2_command_orb),
- DMA_TO_DEVICE);
- last_orb->next_ORB_lo = cpu_to_be32(cmd->command_orb_dma);
- wmb();
- /* Tells hardware that this pointer is valid */
- last_orb->next_ORB_hi = 0;
- dma_sync_single_for_device(hi->host->device.parent,
- last_orb_dma,
- sizeof(struct sbp2_command_orb),
- DMA_TO_DEVICE);
- addr += SBP2_DOORBELL_OFFSET;
- data[0] = 0;
- length = 4;
- }
- lu->last_orb = &cmd->command_orb;
- lu->last_orb_dma = cmd->command_orb_dma;
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
-
- if (sbp2util_node_write_no_wait(lu->ne, addr, data, length)) {
- /*
- * sbp2util_node_write_no_wait failed. We certainly ran out
- * of transaction labels, perhaps just because there were no
- * context switches which gave khpsbpkt a chance to collect
- * free tlabels. Try again in non-atomic context. If necessary,
- * the workqueue job will sleep to guaranteedly get a tlabel.
- * We do not accept new commands until the job is over.
- */
- scsi_block_requests(lu->shost);
- PREPARE_WORK(&lu->protocol_work,
- last_orb ? sbp2util_write_doorbell:
- sbp2util_write_orb_pointer);
- schedule_work(&lu->protocol_work);
- }
-}
-
-static int sbp2_send_command(struct sbp2_lu *lu, struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
-{
- struct sbp2_command_info *cmd;
-
- cmd = sbp2util_allocate_command_orb(lu, SCpnt, done);
- if (!cmd)
- return -EIO;
-
- if (sbp2_create_command_orb(lu, cmd, SCpnt))
- return -ENOMEM;
-
- sbp2_link_orb_command(lu, cmd);
- return 0;
-}
-
-/*
- * Translates SBP-2 status into SCSI sense data for check conditions
- */
-static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status,
- unchar *sense_data)
-{
- /* OK, it's pretty ugly... ;-) */
- sense_data[0] = 0x70;
- sense_data[1] = 0x0;
- sense_data[2] = sbp2_status[9];
- sense_data[3] = sbp2_status[12];
- sense_data[4] = sbp2_status[13];
- sense_data[5] = sbp2_status[14];
- sense_data[6] = sbp2_status[15];
- sense_data[7] = 10;
- sense_data[8] = sbp2_status[16];
- sense_data[9] = sbp2_status[17];
- sense_data[10] = sbp2_status[18];
- sense_data[11] = sbp2_status[19];
- sense_data[12] = sbp2_status[10];
- sense_data[13] = sbp2_status[11];
- sense_data[14] = sbp2_status[20];
- sense_data[15] = sbp2_status[21];
-
- return sbp2_status[8] & 0x3f;
-}
-
-static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid,
- int destid, quadlet_t *data, u64 addr,
- size_t length, u16 fl)
-{
- struct sbp2_fwhost_info *hi;
- struct sbp2_lu *lu = NULL, *lu_tmp;
- struct scsi_cmnd *SCpnt = NULL;
- struct sbp2_status_block *sb;
- u32 scsi_status = SBP2_SCSI_STATUS_GOOD;
- struct sbp2_command_info *cmd;
- unsigned long flags;
-
- if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) {
- SBP2_ERR("Wrong size of status block");
- return RCODE_ADDRESS_ERROR;
- }
- if (unlikely(!host)) {
- SBP2_ERR("host is NULL - this is bad!");
- return RCODE_ADDRESS_ERROR;
- }
- hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
- if (unlikely(!hi)) {
- SBP2_ERR("host info is NULL - this is bad!");
- return RCODE_ADDRESS_ERROR;
- }
-
- /* Find the unit which wrote the status. */
- read_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
- list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) {
- if (lu_tmp->ne->nodeid == nodeid &&
- lu_tmp->status_fifo_addr == addr) {
- lu = lu_tmp;
- break;
- }
- }
- read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
-
- if (unlikely(!lu)) {
- SBP2_ERR("lu is NULL - device is gone?");
- return RCODE_ADDRESS_ERROR;
- }
-
- /* Put response into lu status fifo buffer. The first two bytes
- * come in big endian bit order. Often the target writes only a
- * truncated status block, minimally the first two quadlets. The rest
- * is implied to be zeros. */
- sb = &lu->status_block;
- memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent));
- memcpy(sb, data, length);
- sbp2util_be32_to_cpu_buffer(sb, 8);
-
- /* Ignore unsolicited status. Handle command ORB status. */
- if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2))
- cmd = NULL;
- else
- cmd = sbp2util_find_command_for_orb(lu, sb->ORB_offset_lo);
- if (cmd) {
- /* Grab SCSI command pointers and check status. */
- /*
- * FIXME: If the src field in the status is 1, the ORB DMA must
- * not be reused until status for a subsequent ORB is received.
- */
- SCpnt = cmd->Current_SCpnt;
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- sbp2util_mark_command_completed(lu, cmd);
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
-
- if (SCpnt) {
- u32 h = sb->ORB_offset_hi_misc;
- u32 r = STATUS_GET_RESP(h);
-
- if (r != RESP_STATUS_REQUEST_COMPLETE) {
- SBP2_INFO("resp 0x%x, sbp_status 0x%x",
- r, STATUS_GET_SBP_STATUS(h));
- scsi_status =
- r == RESP_STATUS_TRANSPORT_FAILURE ?
- SBP2_SCSI_STATUS_BUSY :
- SBP2_SCSI_STATUS_COMMAND_TERMINATED;
- }
-
- if (STATUS_GET_LEN(h) > 1)
- scsi_status = sbp2_status_to_sense_data(
- (unchar *)sb, SCpnt->sense_buffer);
-
- if (STATUS_TEST_DEAD(h))
- sbp2_agent_reset(lu, 0);
- }
-
- /* Check here to see if there are no commands in-use. If there
- * are none, we know that the fetch agent left the active state
- * _and_ that we did not reactivate it yet. Therefore clear
- * last_orb so that next time we write directly to the
- * ORB_POINTER register. That way the fetch agent does not need
- * to refetch the next_ORB. */
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- if (list_empty(&lu->cmd_orb_inuse))
- lu->last_orb = NULL;
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
-
- } else {
- /* It's probably status after a management request. */
- if ((sb->ORB_offset_lo == lu->reconnect_orb_dma) ||
- (sb->ORB_offset_lo == lu->login_orb_dma) ||
- (sb->ORB_offset_lo == lu->query_logins_orb_dma) ||
- (sb->ORB_offset_lo == lu->logout_orb_dma)) {
- lu->access_complete = 1;
- wake_up_interruptible(&sbp2_access_wq);
- }
- }
-
- if (SCpnt)
- sbp2scsi_complete_command(lu, scsi_status, SCpnt,
- cmd->Current_done);
- return RCODE_COMPLETE;
-}
-
-/**************************************
- * SCSI interface related section
- **************************************/
-
-static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
-{
- struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
- struct sbp2_fwhost_info *hi;
- int result = DID_NO_CONNECT << 16;
-
- if (unlikely(!sbp2util_node_is_available(lu)))
- goto done;
-
- hi = lu->hi;
-
- if (unlikely(!hi)) {
- SBP2_ERR("sbp2_fwhost_info is NULL - this is bad!");
- goto done;
- }
-
- /* Multiple units are currently represented to the SCSI core as separate
- * targets, not as one target with multiple LUs. Therefore return
- * selection time-out to any IO directed at non-zero LUNs. */
- if (unlikely(SCpnt->device->lun))
- goto done;
-
- if (unlikely(!hpsb_node_entry_valid(lu->ne))) {
- SBP2_ERR("Bus reset in progress - rejecting command");
- result = DID_BUS_BUSY << 16;
- goto done;
- }
-
- /* Bidirectional commands are not yet implemented,
- * and unknown transfer direction not handled. */
- if (unlikely(SCpnt->sc_data_direction == DMA_BIDIRECTIONAL)) {
- SBP2_ERR("Cannot handle DMA_BIDIRECTIONAL - rejecting command");
- result = DID_ERROR << 16;
- goto done;
- }
-
- if (sbp2_send_command(lu, SCpnt, done)) {
- SBP2_ERR("Error sending SCSI command");
- sbp2scsi_complete_command(lu,
- SBP2_SCSI_STATUS_SELECTION_TIMEOUT,
- SCpnt, done);
- }
- return 0;
-
-done:
- SCpnt->result = result;
- done(SCpnt);
- return 0;
-}
-
-static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status)
-{
- struct list_head *lh;
- struct sbp2_command_info *cmd;
- unsigned long flags;
-
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- while (!list_empty(&lu->cmd_orb_inuse)) {
- lh = lu->cmd_orb_inuse.next;
- cmd = list_entry(lh, struct sbp2_command_info, list);
- sbp2util_mark_command_completed(lu, cmd);
- if (cmd->Current_SCpnt) {
- cmd->Current_SCpnt->result = status << 16;
- cmd->Current_done(cmd->Current_SCpnt);
- }
- }
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
-
- return;
-}
-
-/*
- * Complete a regular SCSI command. Can be called in atomic context.
- */
-static void sbp2scsi_complete_command(struct sbp2_lu *lu, u32 scsi_status,
- struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
-{
- if (!SCpnt) {
- SBP2_ERR("SCpnt is NULL");
- return;
- }
-
- switch (scsi_status) {
- case SBP2_SCSI_STATUS_GOOD:
- SCpnt->result = DID_OK << 16;
- break;
-
- case SBP2_SCSI_STATUS_BUSY:
- SBP2_ERR("SBP2_SCSI_STATUS_BUSY");
- SCpnt->result = DID_BUS_BUSY << 16;
- break;
-
- case SBP2_SCSI_STATUS_CHECK_CONDITION:
- SCpnt->result = CHECK_CONDITION << 1 | DID_OK << 16;
- break;
-
- case SBP2_SCSI_STATUS_SELECTION_TIMEOUT:
- SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT");
- SCpnt->result = DID_NO_CONNECT << 16;
- scsi_print_command(SCpnt);
- break;
-
- case SBP2_SCSI_STATUS_CONDITION_MET:
- case SBP2_SCSI_STATUS_RESERVATION_CONFLICT:
- case SBP2_SCSI_STATUS_COMMAND_TERMINATED:
- SBP2_ERR("Bad SCSI status = %x", scsi_status);
- SCpnt->result = DID_ERROR << 16;
- scsi_print_command(SCpnt);
- break;
-
- default:
- SBP2_ERR("Unsupported SCSI status = %x", scsi_status);
- SCpnt->result = DID_ERROR << 16;
- }
-
- /* If a bus reset is in progress and there was an error, complete
- * the command as busy so that it will get retried. */
- if (!hpsb_node_entry_valid(lu->ne)
- && (scsi_status != SBP2_SCSI_STATUS_GOOD)) {
- SBP2_ERR("Completing command with busy (bus reset)");
- SCpnt->result = DID_BUS_BUSY << 16;
- }
-
- /* Tell the SCSI stack that we're done with this command. */
- done(SCpnt);
-}
-
-static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
-{
- struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];
-
- if (sdev->lun != 0 || sdev->id != lu->ud->id || sdev->channel != 0)
- return -ENODEV;
-
- lu->sdev = sdev;
- sdev->allow_restart = 1;
-
- /* SBP-2 requires quadlet alignment of the data buffers. */
- blk_queue_update_dma_alignment(sdev->request_queue, 4 - 1);
-
- if (lu->workarounds & SBP2_WORKAROUND_INQUIRY_36)
- sdev->inquiry_len = 36;
- return 0;
-}
-
-static int sbp2scsi_slave_configure(struct scsi_device *sdev)
-{
- struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];
-
- sdev->use_10_for_rw = 1;
-
- if (sbp2_exclusive_login)
- sdev->manage_start_stop = 1;
- if (sdev->type == TYPE_ROM)
- sdev->use_10_for_ms = 1;
- if (sdev->type == TYPE_DISK &&
- lu->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
- sdev->skip_ms_page_8 = 1;
- if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
- sdev->fix_capacity = 1;
- if (lu->workarounds & SBP2_WORKAROUND_POWER_CONDITION)
- sdev->start_stop_pwr_cond = 1;
- if (lu->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
- blk_queue_max_hw_sectors(sdev->request_queue, 128 * 1024 / 512);
-
- blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE);
- return 0;
-}
-
-static void sbp2scsi_slave_destroy(struct scsi_device *sdev)
-{
- ((struct sbp2_lu *)sdev->host->hostdata[0])->sdev = NULL;
- return;
-}
-
-/*
- * Called by scsi stack when something has really gone wrong.
- * Usually called when a command has timed-out for some reason.
- */
-static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
-{
- struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
- struct sbp2_command_info *cmd;
- unsigned long flags;
-
- SBP2_INFO("aborting sbp2 command");
- scsi_print_command(SCpnt);
-
- if (sbp2util_node_is_available(lu)) {
- sbp2_agent_reset(lu, 1);
-
- /* Return a matching command structure to the free pool. */
- spin_lock_irqsave(&lu->cmd_orb_lock, flags);
- cmd = sbp2util_find_command_for_SCpnt(lu, SCpnt);
- if (cmd) {
- sbp2util_mark_command_completed(lu, cmd);
- if (cmd->Current_SCpnt) {
- cmd->Current_SCpnt->result = DID_ABORT << 16;
- cmd->Current_done(cmd->Current_SCpnt);
- }
- }
- spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
-
- sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY);
- }
-
- return SUCCESS;
-}
-
-/*
- * Called by scsi stack when something has really gone wrong.
- */
-static int sbp2scsi_reset(struct scsi_cmnd *SCpnt)
-{
- struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
-
- SBP2_INFO("reset requested");
-
- if (sbp2util_node_is_available(lu)) {
- SBP2_INFO("generating sbp2 fetch agent reset");
- sbp2_agent_reset(lu, 1);
- }
-
- return SUCCESS;
-}
-
-static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct scsi_device *sdev;
- struct sbp2_lu *lu;
-
- if (!(sdev = to_scsi_device(dev)))
- return 0;
-
- if (!(lu = (struct sbp2_lu *)sdev->host->hostdata[0]))
- return 0;
-
- if (sbp2_long_sysfs_ieee1394_id)
- return sprintf(buf, "%016Lx:%06x:%04x\n",
- (unsigned long long)lu->ne->guid,
- lu->ud->directory_id, ORB_SET_LUN(lu->lun));
- else
- return sprintf(buf, "%016Lx:%d:%d\n",
- (unsigned long long)lu->ne->guid,
- lu->ud->id, ORB_SET_LUN(lu->lun));
-}
-
-MODULE_AUTHOR("Ben Collins <bcollins@debian.org>");
-MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver");
-MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME);
-MODULE_LICENSE("GPL");
-
-static int sbp2_module_init(void)
-{
- int ret;
-
- if (sbp2_serialize_io) {
- sbp2_shost_template.can_queue = 1;
- sbp2_shost_template.cmd_per_lun = 1;
- }
-
- sbp2_shost_template.max_sectors = sbp2_max_sectors;
-
- hpsb_register_highlevel(&sbp2_highlevel);
- ret = hpsb_register_protocol(&sbp2_driver);
- if (ret) {
- SBP2_ERR("Failed to register protocol");
- hpsb_unregister_highlevel(&sbp2_highlevel);
- return ret;
- }
- return 0;
-}
-
-static void __exit sbp2_module_exit(void)
-{
- hpsb_unregister_protocol(&sbp2_driver);
- hpsb_unregister_highlevel(&sbp2_highlevel);
-}
-
-module_init(sbp2_module_init);
-module_exit(sbp2_module_exit);
diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h
deleted file mode 100644
index 64a3a66a8a39..000000000000
--- a/drivers/ieee1394/sbp2.h
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * sbp2.h - Defines and prototypes for sbp2.c
- *
- * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com)
- * jamesg@filanet.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.
- */
-
-#ifndef SBP2_H
-#define SBP2_H
-
-#define SBP2_DEVICE_NAME "sbp2"
-
-/*
- * There is no transport protocol limit to the CDB length, but we implement
- * a fixed length only. 16 bytes is enough for disks larger than 2 TB.
- */
-#define SBP2_MAX_CDB_SIZE 16
-
-/*
- * SBP-2 specific definitions
- */
-
-#define ORB_DIRECTION_WRITE_TO_MEDIA 0x0
-#define ORB_DIRECTION_READ_FROM_MEDIA 0x1
-#define ORB_DIRECTION_NO_DATA_TRANSFER 0x2
-
-#define ORB_SET_NULL_PTR(v) (((v) & 0x1) << 31)
-#define ORB_SET_NOTIFY(v) (((v) & 0x1) << 31)
-#define ORB_SET_RQ_FMT(v) (((v) & 0x3) << 29)
-#define ORB_SET_NODE_ID(v) (((v) & 0xffff) << 16)
-#define ORB_SET_STATUS_FIFO_HI(v, id) ((v) >> 32 | ORB_SET_NODE_ID(id))
-#define ORB_SET_STATUS_FIFO_LO(v) ((v) & 0xffffffff)
-#define ORB_SET_DATA_SIZE(v) ((v) & 0xffff)
-#define ORB_SET_PAGE_SIZE(v) (((v) & 0x7) << 16)
-#define ORB_SET_PAGE_TABLE_PRESENT(v) (((v) & 0x1) << 19)
-#define ORB_SET_MAX_PAYLOAD(v) (((v) & 0xf) << 20)
-#define ORB_SET_SPEED(v) (((v) & 0x7) << 24)
-#define ORB_SET_DIRECTION(v) (((v) & 0x1) << 27)
-
-struct sbp2_command_orb {
- u32 next_ORB_hi;
- u32 next_ORB_lo;
- u32 data_descriptor_hi;
- u32 data_descriptor_lo;
- u32 misc;
- u8 cdb[SBP2_MAX_CDB_SIZE];
-} __attribute__((packed));
-
-#define SBP2_LOGIN_REQUEST 0x0
-#define SBP2_QUERY_LOGINS_REQUEST 0x1
-#define SBP2_RECONNECT_REQUEST 0x3
-#define SBP2_SET_PASSWORD_REQUEST 0x4
-#define SBP2_LOGOUT_REQUEST 0x7
-#define SBP2_ABORT_TASK_REQUEST 0xb
-#define SBP2_ABORT_TASK_SET 0xc
-#define SBP2_LOGICAL_UNIT_RESET 0xe
-#define SBP2_TARGET_RESET_REQUEST 0xf
-
-#define ORB_SET_LUN(v) ((v) & 0xffff)
-#define ORB_SET_FUNCTION(v) (((v) & 0xf) << 16)
-#define ORB_SET_RECONNECT(v) (((v) & 0xf) << 20)
-#define ORB_SET_EXCLUSIVE(v) ((v) ? 1 << 28 : 0)
-#define ORB_SET_LOGIN_RESP_LENGTH(v) ((v) & 0xffff)
-#define ORB_SET_PASSWD_LENGTH(v) (((v) & 0xffff) << 16)
-
-struct sbp2_login_orb {
- u32 password_hi;
- u32 password_lo;
- u32 login_response_hi;
- u32 login_response_lo;
- u32 lun_misc;
- u32 passwd_resp_lengths;
- u32 status_fifo_hi;
- u32 status_fifo_lo;
-} __attribute__((packed));
-
-#define RESPONSE_GET_LOGIN_ID(v) ((v) & 0xffff)
-#define RESPONSE_GET_LENGTH(v) (((v) >> 16) & 0xffff)
-#define RESPONSE_GET_RECONNECT_HOLD(v) ((v) & 0xffff)
-
-struct sbp2_login_response {
- u32 length_login_ID;
- u32 command_block_agent_hi;
- u32 command_block_agent_lo;
- u32 reconnect_hold;
-} __attribute__((packed));
-
-#define ORB_SET_LOGIN_ID(v) ((v) & 0xffff)
-#define ORB_SET_QUERY_LOGINS_RESP_LENGTH(v) ((v) & 0xffff)
-
-struct sbp2_query_logins_orb {
- u32 reserved1;
- u32 reserved2;
- u32 query_response_hi;
- u32 query_response_lo;
- u32 lun_misc;
- u32 reserved_resp_length;
- u32 status_fifo_hi;
- u32 status_fifo_lo;
-} __attribute__((packed));
-
-#define RESPONSE_GET_MAX_LOGINS(v) ((v) & 0xffff)
-#define RESPONSE_GET_ACTIVE_LOGINS(v) ((RESPONSE_GET_LENGTH((v)) - 4) / 12)
-
-struct sbp2_query_logins_response {
- u32 length_max_logins;
- u32 misc_IDs;
- u32 initiator_misc_hi;
- u32 initiator_misc_lo;
-} __attribute__((packed));
-
-struct sbp2_reconnect_orb {
- u32 reserved1;
- u32 reserved2;
- u32 reserved3;
- u32 reserved4;
- u32 login_ID_misc;
- u32 reserved5;
- u32 status_fifo_hi;
- u32 status_fifo_lo;
-} __attribute__((packed));
-
-struct sbp2_logout_orb {
- u32 reserved1;
- u32 reserved2;
- u32 reserved3;
- u32 reserved4;
- u32 login_ID_misc;
- u32 reserved5;
- u32 status_fifo_hi;
- u32 status_fifo_lo;
-} __attribute__((packed));
-
-struct sbp2_unrestricted_page_table {
- __be32 high;
- __be32 low;
-};
-
-#define RESP_STATUS_REQUEST_COMPLETE 0x0
-#define RESP_STATUS_TRANSPORT_FAILURE 0x1
-#define RESP_STATUS_ILLEGAL_REQUEST 0x2
-#define RESP_STATUS_VENDOR_DEPENDENT 0x3
-
-#define SBP2_STATUS_NO_ADDITIONAL_INFO 0x0
-#define SBP2_STATUS_REQ_TYPE_NOT_SUPPORTED 0x1
-#define SBP2_STATUS_SPEED_NOT_SUPPORTED 0x2
-#define SBP2_STATUS_PAGE_SIZE_NOT_SUPPORTED 0x3
-#define SBP2_STATUS_ACCESS_DENIED 0x4
-#define SBP2_STATUS_LU_NOT_SUPPORTED 0x5
-#define SBP2_STATUS_MAX_PAYLOAD_TOO_SMALL 0x6
-#define SBP2_STATUS_RESOURCES_UNAVAILABLE 0x8
-#define SBP2_STATUS_FUNCTION_REJECTED 0x9
-#define SBP2_STATUS_LOGIN_ID_NOT_RECOGNIZED 0xa
-#define SBP2_STATUS_DUMMY_ORB_COMPLETED 0xb
-#define SBP2_STATUS_REQUEST_ABORTED 0xc
-#define SBP2_STATUS_UNSPECIFIED_ERROR 0xff
-
-#define SFMT_CURRENT_ERROR 0x0
-#define SFMT_DEFERRED_ERROR 0x1
-#define SFMT_VENDOR_DEPENDENT_STATUS 0x3
-
-#define STATUS_GET_SRC(v) (((v) >> 30) & 0x3)
-#define STATUS_GET_RESP(v) (((v) >> 28) & 0x3)
-#define STATUS_GET_LEN(v) (((v) >> 24) & 0x7)
-#define STATUS_GET_SBP_STATUS(v) (((v) >> 16) & 0xff)
-#define STATUS_GET_ORB_OFFSET_HI(v) ((v) & 0x0000ffff)
-#define STATUS_TEST_DEAD(v) ((v) & 0x08000000)
-/* test 'resp' | 'dead' | 'sbp2_status' */
-#define STATUS_TEST_RDS(v) ((v) & 0x38ff0000)
-
-struct sbp2_status_block {
- u32 ORB_offset_hi_misc;
- u32 ORB_offset_lo;
- u8 command_set_dependent[24];
-} __attribute__((packed));
-
-
-/*
- * SBP2 related configuration ROM definitions
- */
-
-#define SBP2_UNIT_DIRECTORY_OFFSET_KEY 0xd1
-#define SBP2_CSR_OFFSET_KEY 0x54
-#define SBP2_UNIT_SPEC_ID_KEY 0x12
-#define SBP2_UNIT_SW_VERSION_KEY 0x13
-#define SBP2_COMMAND_SET_SPEC_ID_KEY 0x38
-#define SBP2_COMMAND_SET_KEY 0x39
-#define SBP2_UNIT_CHARACTERISTICS_KEY 0x3a
-#define SBP2_DEVICE_TYPE_AND_LUN_KEY 0x14
-#define SBP2_FIRMWARE_REVISION_KEY 0x3c
-
-#define SBP2_AGENT_STATE_OFFSET 0x00ULL
-#define SBP2_AGENT_RESET_OFFSET 0x04ULL
-#define SBP2_ORB_POINTER_OFFSET 0x08ULL
-#define SBP2_DOORBELL_OFFSET 0x10ULL
-#define SBP2_UNSOLICITED_STATUS_ENABLE_OFFSET 0x14ULL
-#define SBP2_UNSOLICITED_STATUS_VALUE 0xf
-
-#define SBP2_BUSY_TIMEOUT_ADDRESS 0xfffff0000210ULL
-/* biggest possible value for Single Phase Retry count is 0xf */
-#define SBP2_BUSY_TIMEOUT_VALUE 0xf
-
-#define SBP2_AGENT_RESET_DATA 0xf
-
-#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e
-#define SBP2_SW_VERSION_ENTRY 0x00010483
-
-/*
- * The default maximum s/g segment size of a FireWire controller is
- * usually 0x10000, but SBP-2 only allows 0xffff. Since buffers have to
- * be quadlet-aligned, we set the length limit to 0xffff & ~3.
- */
-#define SBP2_MAX_SEG_SIZE 0xfffc
-
-/*
- * There is no real limitation of the queue depth (i.e. length of the linked
- * list of command ORBs) at the target. The chosen depth is merely an
- * implementation detail of the sbp2 driver.
- */
-#define SBP2_MAX_CMDS 8
-
-#define SBP2_SCSI_STATUS_GOOD 0x0
-#define SBP2_SCSI_STATUS_CHECK_CONDITION 0x2
-#define SBP2_SCSI_STATUS_CONDITION_MET 0x4
-#define SBP2_SCSI_STATUS_BUSY 0x8
-#define SBP2_SCSI_STATUS_RESERVATION_CONFLICT 0x18
-#define SBP2_SCSI_STATUS_COMMAND_TERMINATED 0x22
-#define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff
-
-
-/*
- * Representations of commands and devices
- */
-
-/* Per SCSI command */
-struct sbp2_command_info {
- struct list_head list;
- struct sbp2_command_orb command_orb;
- dma_addr_t command_orb_dma;
- struct scsi_cmnd *Current_SCpnt;
- void (*Current_done)(struct scsi_cmnd *);
-
- /* Also need s/g structure for each sbp2 command */
- struct sbp2_unrestricted_page_table
- scatter_gather_element[SG_ALL] __attribute__((aligned(8)));
- dma_addr_t sge_dma;
-};
-
-/* Per FireWire host */
-struct sbp2_fwhost_info {
- struct hpsb_host *host;
- struct list_head logical_units;
-};
-
-/* Per logical unit */
-struct sbp2_lu {
- /* Operation request blocks */
- struct sbp2_command_orb *last_orb;
- dma_addr_t last_orb_dma;
- struct sbp2_login_orb *login_orb;
- dma_addr_t login_orb_dma;
- struct sbp2_login_response *login_response;
- dma_addr_t login_response_dma;
- struct sbp2_query_logins_orb *query_logins_orb;
- dma_addr_t query_logins_orb_dma;
- struct sbp2_query_logins_response *query_logins_response;
- dma_addr_t query_logins_response_dma;
- struct sbp2_reconnect_orb *reconnect_orb;
- dma_addr_t reconnect_orb_dma;
- struct sbp2_logout_orb *logout_orb;
- dma_addr_t logout_orb_dma;
- struct sbp2_status_block status_block;
-
- /* How to talk to the unit */
- u64 management_agent_addr;
- u64 command_block_agent_addr;
- u32 speed_code;
- u32 max_payload_size;
- u16 lun;
-
- /* Address for the unit to write status blocks to */
- u64 status_fifo_addr;
-
- /* Waitqueue flag for logins, reconnects, logouts, query logins */
- unsigned int access_complete:1;
-
- /* Pool of command ORBs for this logical unit */
- spinlock_t cmd_orb_lock;
- struct list_head cmd_orb_inuse;
- struct list_head cmd_orb_completed;
-
- /* Backlink to FireWire host; list of units attached to the host */
- struct sbp2_fwhost_info *hi;
- struct list_head lu_list;
-
- /* IEEE 1394 core's device representations */
- struct node_entry *ne;
- struct unit_directory *ud;
-
- /* SCSI core's device representations */
- struct scsi_device *sdev;
- struct Scsi_Host *shost;
-
- /* Device specific workarounds/brokeness */
- unsigned workarounds;
-
- /* Connection state */
- atomic_t state;
-
- /* For deferred requests to the fetch agent */
- struct work_struct protocol_work;
-};
-
-/* For use in sbp2_lu.state */
-enum sbp2lu_state_types {
- SBP2LU_STATE_RUNNING, /* all normal */
- SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */
- SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */
-};
-
-/* For use in sbp2_lu.workarounds and in the corresponding
- * module load parameter */
-#define SBP2_WORKAROUND_128K_MAX_TRANS 0x1
-#define SBP2_WORKAROUND_INQUIRY_36 0x2
-#define SBP2_WORKAROUND_MODE_SENSE_8 0x4
-#define SBP2_WORKAROUND_FIX_CAPACITY 0x8
-#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
-#define SBP2_INQUIRY_DELAY 12
-#define SBP2_WORKAROUND_POWER_CONDITION 0x20
-#define SBP2_WORKAROUND_OVERRIDE 0x100
-
-#endif /* SBP2_H */
diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c
deleted file mode 100644
index 5c74f796d7f1..000000000000
--- a/drivers/ieee1394/video1394.c
+++ /dev/null
@@ -1,1528 +0,0 @@
-/*
- * video1394.c - video driver for OHCI 1394 boards
- * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
- * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * NOTES:
- *
- * ioctl return codes:
- * EFAULT is only for invalid address for the argp
- * EINVAL for out of range values
- * EBUSY when trying to use an already used resource
- * ESRCH when trying to free/stop a not used resource
- * EAGAIN for resource allocation failure that could perhaps succeed later
- * ENOTTY for unsupported ioctl request
- *
- */
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-#include <linux/types.h>
-#include <linux/vmalloc.h>
-#include <linux/timex.h>
-#include <linux/mm.h>
-#include <linux/compat.h>
-#include <linux/cdev.h>
-
-#include "dma.h"
-#include "highlevel.h"
-#include "hosts.h"
-#include "ieee1394.h"
-#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
-#include "ieee1394_types.h"
-#include "nodemgr.h"
-#include "ohci1394.h"
-#include "video1394.h"
-
-#define ISO_CHANNELS 64
-
-struct it_dma_prg {
- struct dma_cmd begin;
- quadlet_t data[4];
- struct dma_cmd end;
- quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */
-};
-
-struct dma_iso_ctx {
- struct ti_ohci *ohci;
- int type; /* OHCI_ISO_TRANSMIT or OHCI_ISO_RECEIVE */
- struct ohci1394_iso_tasklet iso_tasklet;
- int channel;
- int ctx;
- int last_buffer;
- int * next_buffer; /* For ISO Transmit of video packets
- to write the correct SYT field
- into the next block */
- unsigned int num_desc;
- unsigned int buf_size;
- unsigned int frame_size;
- unsigned int packet_size;
- unsigned int left_size;
- unsigned int nb_cmd;
-
- struct dma_region dma;
-
- struct dma_prog_region *prg_reg;
-
- struct dma_cmd **ir_prg;
- struct it_dma_prg **it_prg;
-
- unsigned int *buffer_status;
- unsigned int *buffer_prg_assignment;
- struct timeval *buffer_time; /* time when the buffer was received */
- unsigned int *last_used_cmd; /* For ISO Transmit with
- variable sized packets only ! */
- int ctrlClear;
- int ctrlSet;
- int cmdPtr;
- int ctxMatch;
- wait_queue_head_t waitq;
- spinlock_t lock;
- unsigned int syt_offset;
- int flags;
-
- struct list_head link;
-};
-
-
-struct file_ctx {
- struct ti_ohci *ohci;
- struct list_head context_list;
- struct dma_iso_ctx *current_ctx;
-};
-
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-#define VIDEO1394_DEBUG
-#endif
-
-#ifdef DBGMSG
-#undef DBGMSG
-#endif
-
-#ifdef VIDEO1394_DEBUG
-#define DBGMSG(card, fmt, args...) \
-printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args)
-#else
-#define DBGMSG(card, fmt, args...) do {} while (0)
-#endif
-
-/* print general (card independent) information */
-#define PRINT_G(level, fmt, args...) \
-printk(level "video1394: " fmt "\n" , ## args)
-
-/* print card specific information */
-#define PRINT(level, card, fmt, args...) \
-printk(level "video1394_%d: " fmt "\n" , card , ## args)
-
-static void wakeup_dma_ir_ctx(unsigned long l);
-static void wakeup_dma_it_ctx(unsigned long l);
-
-static struct hpsb_highlevel video1394_highlevel;
-
-static int free_dma_iso_ctx(struct dma_iso_ctx *d)
-{
- int i;
-
- DBGMSG(d->ohci->host->id, "Freeing dma_iso_ctx %d", d->ctx);
-
- ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
- if (d->iso_tasklet.link.next != NULL)
- ohci1394_unregister_iso_tasklet(d->ohci, &d->iso_tasklet);
-
- dma_region_free(&d->dma);
-
- if (d->prg_reg) {
- for (i = 0; i < d->num_desc; i++)
- dma_prog_region_free(&d->prg_reg[i]);
- kfree(d->prg_reg);
- }
-
- kfree(d->ir_prg);
- kfree(d->it_prg);
- kfree(d->buffer_status);
- kfree(d->buffer_prg_assignment);
- kfree(d->buffer_time);
- kfree(d->last_used_cmd);
- kfree(d->next_buffer);
- list_del(&d->link);
- kfree(d);
-
- return 0;
-}
-
-static struct dma_iso_ctx *
-alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
- int buf_size, int channel, unsigned int packet_size)
-{
- struct dma_iso_ctx *d;
- int i;
-
- d = kzalloc(sizeof(*d), GFP_KERNEL);
- if (!d) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma_iso_ctx");
- return NULL;
- }
-
- d->ohci = ohci;
- d->type = type;
- d->channel = channel;
- d->num_desc = num_desc;
- d->frame_size = buf_size;
- d->buf_size = PAGE_ALIGN(buf_size);
- d->last_buffer = -1;
- INIT_LIST_HEAD(&d->link);
- init_waitqueue_head(&d->waitq);
-
- /* Init the regions for easy cleanup */
- dma_region_init(&d->dma);
-
- if (dma_region_alloc(&d->dma, (d->num_desc - 1) * d->buf_size, ohci->dev,
- PCI_DMA_BIDIRECTIONAL)) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer");
- free_dma_iso_ctx(d);
- return NULL;
- }
-
- if (type == OHCI_ISO_RECEIVE)
- ohci1394_init_iso_tasklet(&d->iso_tasklet, type,
- wakeup_dma_ir_ctx,
- (unsigned long) d);
- else
- ohci1394_init_iso_tasklet(&d->iso_tasklet, type,
- wakeup_dma_it_ctx,
- (unsigned long) d);
-
- if (ohci1394_register_iso_tasklet(ohci, &d->iso_tasklet) < 0) {
- PRINT(KERN_ERR, ohci->host->id, "no free iso %s contexts",
- type == OHCI_ISO_RECEIVE ? "receive" : "transmit");
- free_dma_iso_ctx(d);
- return NULL;
- }
- d->ctx = d->iso_tasklet.context;
-
- d->prg_reg = kmalloc(d->num_desc * sizeof(*d->prg_reg), GFP_KERNEL);
- if (!d->prg_reg) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate ir prg regs");
- free_dma_iso_ctx(d);
- return NULL;
- }
- /* Makes for easier cleanup */
- for (i = 0; i < d->num_desc; i++)
- dma_prog_region_init(&d->prg_reg[i]);
-
- if (type == OHCI_ISO_RECEIVE) {
- d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx;
- d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx;
- d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx;
- d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx;
-
- d->ir_prg = kzalloc(d->num_desc * sizeof(*d->ir_prg),
- GFP_KERNEL);
-
- if (!d->ir_prg) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg");
- free_dma_iso_ctx(d);
- return NULL;
- }
-
- d->nb_cmd = d->buf_size / PAGE_SIZE + 1;
- d->left_size = (d->frame_size % PAGE_SIZE) ?
- d->frame_size % PAGE_SIZE : PAGE_SIZE;
-
- for (i = 0;i < d->num_desc; i++) {
- if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd *
- sizeof(struct dma_cmd), ohci->dev)) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg");
- free_dma_iso_ctx(d);
- return NULL;
- }
- d->ir_prg[i] = (struct dma_cmd *)d->prg_reg[i].kvirt;
- }
-
- } else { /* OHCI_ISO_TRANSMIT */
- d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx;
- d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx;
- d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx;
-
- d->it_prg = kzalloc(d->num_desc * sizeof(*d->it_prg),
- GFP_KERNEL);
-
- if (!d->it_prg) {
- PRINT(KERN_ERR, ohci->host->id,
- "Failed to allocate dma it prg");
- free_dma_iso_ctx(d);
- return NULL;
- }
-
- d->packet_size = packet_size;
-
- if (PAGE_SIZE % packet_size || packet_size>4096) {
- PRINT(KERN_ERR, ohci->host->id,
- "Packet size %d (page_size: %ld) "
- "not yet supported\n",
- packet_size, PAGE_SIZE);
- free_dma_iso_ctx(d);
- return NULL;
- }
-
- d->nb_cmd = d->frame_size / d->packet_size;
- if (d->frame_size % d->packet_size) {
- d->nb_cmd++;
- d->left_size = d->frame_size % d->packet_size;
- } else
- d->left_size = d->packet_size;
-
- for (i = 0; i < d->num_desc; i++) {
- if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd *
- sizeof(struct it_dma_prg), ohci->dev)) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma it prg");
- free_dma_iso_ctx(d);
- return NULL;
- }
- d->it_prg[i] = (struct it_dma_prg *)d->prg_reg[i].kvirt;
- }
- }
-
- d->buffer_status =
- kzalloc(d->num_desc * sizeof(*d->buffer_status), GFP_KERNEL);
- d->buffer_prg_assignment =
- kzalloc(d->num_desc * sizeof(*d->buffer_prg_assignment), GFP_KERNEL);
- d->buffer_time =
- kzalloc(d->num_desc * sizeof(*d->buffer_time), GFP_KERNEL);
- d->last_used_cmd =
- kzalloc(d->num_desc * sizeof(*d->last_used_cmd), GFP_KERNEL);
- d->next_buffer =
- kzalloc(d->num_desc * sizeof(*d->next_buffer), GFP_KERNEL);
-
- if (!d->buffer_status || !d->buffer_prg_assignment || !d->buffer_time ||
- !d->last_used_cmd || !d->next_buffer) {
- PRINT(KERN_ERR, ohci->host->id,
- "Failed to allocate dma_iso_ctx member");
- free_dma_iso_ctx(d);
- return NULL;
- }
-
- spin_lock_init(&d->lock);
-
- DBGMSG(ohci->host->id, "Iso %s DMA: %d buffers "
- "of size %d allocated for a frame size %d, each with %d prgs",
- (type == OHCI_ISO_RECEIVE) ? "receive" : "transmit",
- d->num_desc - 1, d->buf_size, d->frame_size, d->nb_cmd);
-
- return d;
-}
-
-static void reset_ir_status(struct dma_iso_ctx *d, int n)
-{
- int i;
- d->ir_prg[n][0].status = cpu_to_le32(4);
- d->ir_prg[n][1].status = cpu_to_le32(PAGE_SIZE-4);
- for (i = 2; i < d->nb_cmd - 1; i++)
- d->ir_prg[n][i].status = cpu_to_le32(PAGE_SIZE);
- d->ir_prg[n][i].status = cpu_to_le32(d->left_size);
-}
-
-static void reprogram_dma_ir_prg(struct dma_iso_ctx *d, int n, int buffer, int flags)
-{
- struct dma_cmd *ir_prg = d->ir_prg[n];
- unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
- int i;
-
- d->buffer_prg_assignment[n] = buffer;
-
- ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf -
- (unsigned long)d->dma.kvirt));
- ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
- (buf + 4) - (unsigned long)d->dma.kvirt));
-
- for (i=2;i<d->nb_cmd-1;i++) {
- ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
- (buf+(i-1)*PAGE_SIZE) -
- (unsigned long)d->dma.kvirt));
- }
-
- ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
- DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size);
- ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
- (buf+(i-1)*PAGE_SIZE) - (unsigned long)d->dma.kvirt));
-}
-
-static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags)
-{
- struct dma_cmd *ir_prg = d->ir_prg[n];
- struct dma_prog_region *ir_reg = &d->prg_reg[n];
- unsigned long buf = (unsigned long)d->dma.kvirt;
- int i;
-
- /* the first descriptor will read only 4 bytes */
- ir_prg[0].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
- DMA_CTL_BRANCH | 4);
-
- /* set the sync flag */
- if (flags & VIDEO1394_SYNC_FRAMES)
- ir_prg[0].control |= cpu_to_le32(DMA_CTL_WAIT);
-
- ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf -
- (unsigned long)d->dma.kvirt));
- ir_prg[0].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
- 1 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
-
- /* If there is *not* only one DMA page per frame (hence, d->nb_cmd==2) */
- if (d->nb_cmd > 2) {
- /* The second descriptor will read PAGE_SIZE-4 bytes */
- ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
- DMA_CTL_BRANCH | (PAGE_SIZE-4));
- ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) -
- (unsigned long)d->dma.kvirt));
- ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
- 2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
-
- for (i = 2; i < d->nb_cmd - 1; i++) {
- ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
- DMA_CTL_BRANCH | PAGE_SIZE);
- ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
- (buf+(i-1)*PAGE_SIZE) -
- (unsigned long)d->dma.kvirt));
-
- ir_prg[i].branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
- (i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
- }
-
- /* The last descriptor will generate an interrupt */
- ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
- DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size);
- ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
- (buf+(i-1)*PAGE_SIZE) -
- (unsigned long)d->dma.kvirt));
- } else {
- /* Only one DMA page is used. Read d->left_size immediately and */
- /* generate an interrupt as this is also the last page. */
- ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
- DMA_CTL_IRQ | DMA_CTL_BRANCH | (d->left_size-4));
- ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
- (buf + 4) - (unsigned long)d->dma.kvirt));
- }
-}
-
-static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags)
-{
- struct ti_ohci *ohci = (struct ti_ohci *)d->ohci;
- int i;
-
- d->flags = flags;
-
- ohci1394_stop_context(ohci, d->ctrlClear, NULL);
-
- for (i=0;i<d->num_desc;i++) {
- initialize_dma_ir_prg(d, i, flags);
- reset_ir_status(d, i);
- }
-
- /* reset the ctrl register */
- reg_write(ohci, d->ctrlClear, 0xf0000000);
-
- /* Set bufferFill */
- reg_write(ohci, d->ctrlSet, 0x80000000);
-
- /* Set isoch header */
- if (flags & VIDEO1394_INCLUDE_ISO_HEADERS)
- reg_write(ohci, d->ctrlSet, 0x40000000);
-
- /* Set the context match register to match on all tags,
- sync for sync tag, and listen to d->channel */
- reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel);
-
- /* Set up isoRecvIntMask to generate interrupts */
- reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx);
-}
-
-/* find which context is listening to this channel */
-static struct dma_iso_ctx *
-find_ctx(struct list_head *list, int type, int channel)
-{
- struct dma_iso_ctx *ctx;
-
- list_for_each_entry(ctx, list, link) {
- if (ctx->type == type && ctx->channel == channel)
- return ctx;
- }
-
- return NULL;
-}
-
-static void wakeup_dma_ir_ctx(unsigned long l)
-{
- struct dma_iso_ctx *d = (struct dma_iso_ctx *) l;
- int i;
-
- spin_lock(&d->lock);
-
- for (i = 0; i < d->num_desc; i++) {
- if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) {
- reset_ir_status(d, i);
- d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
- do_gettimeofday(&d->buffer_time[d->buffer_prg_assignment[i]]);
- dma_region_sync_for_cpu(&d->dma,
- d->buffer_prg_assignment[i] * d->buf_size,
- d->buf_size);
- }
- }
-
- spin_unlock(&d->lock);
-
- if (waitqueue_active(&d->waitq))
- wake_up_interruptible(&d->waitq);
-}
-
-static inline void put_timestamp(struct ti_ohci *ohci, struct dma_iso_ctx * d,
- int n)
-{
- unsigned char* buf = d->dma.kvirt + n * d->buf_size;
- u32 cycleTimer;
- u32 timeStamp;
-
- if (n == -1) {
- return;
- }
-
- cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
-
- timeStamp = ((cycleTimer & 0x0fff) + d->syt_offset); /* 11059 = 450 us */
- timeStamp = (timeStamp % 3072 + ((timeStamp / 3072) << 12)
- + (cycleTimer & 0xf000)) & 0xffff;
-
- buf[6] = timeStamp >> 8;
- buf[7] = timeStamp & 0xff;
-
- /* if first packet is empty packet, then put timestamp into the next full one too */
- if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) {
- buf += d->packet_size;
- buf[6] = timeStamp >> 8;
- buf[7] = timeStamp & 0xff;
- }
-
- /* do the next buffer frame too in case of irq latency */
- n = d->next_buffer[n];
- if (n == -1) {
- return;
- }
- buf = d->dma.kvirt + n * d->buf_size;
-
- timeStamp += (d->last_used_cmd[n] << 12) & 0xffff;
-
- buf[6] = timeStamp >> 8;
- buf[7] = timeStamp & 0xff;
-
- /* if first packet is empty packet, then put timestamp into the next full one too */
- if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) {
- buf += d->packet_size;
- buf[6] = timeStamp >> 8;
- buf[7] = timeStamp & 0xff;
- }
-
-#if 0
- printk("curr: %d, next: %d, cycleTimer: %08x timeStamp: %08x\n",
- curr, n, cycleTimer, timeStamp);
-#endif
-}
-
-static void wakeup_dma_it_ctx(unsigned long l)
-{
- struct dma_iso_ctx *d = (struct dma_iso_ctx *) l;
- struct ti_ohci *ohci = d->ohci;
- int i;
-
- spin_lock(&d->lock);
-
- for (i = 0; i < d->num_desc; i++) {
- if (d->it_prg[i][d->last_used_cmd[i]].end.status &
- cpu_to_le32(0xFFFF0000)) {
- int next = d->next_buffer[i];
- put_timestamp(ohci, d, next);
- d->it_prg[i][d->last_used_cmd[i]].end.status = 0;
- d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
- }
- }
-
- spin_unlock(&d->lock);
-
- if (waitqueue_active(&d->waitq))
- wake_up_interruptible(&d->waitq);
-}
-
-static void reprogram_dma_it_prg(struct dma_iso_ctx *d, int n, int buffer)
-{
- struct it_dma_prg *it_prg = d->it_prg[n];
- unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
- int i;
-
- d->buffer_prg_assignment[n] = buffer;
- for (i=0;i<d->nb_cmd;i++) {
- it_prg[i].end.address =
- cpu_to_le32(dma_region_offset_to_bus(&d->dma,
- (buf+i*d->packet_size) - (unsigned long)d->dma.kvirt));
- }
-}
-
-static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag)
-{
- struct it_dma_prg *it_prg = d->it_prg[n];
- struct dma_prog_region *it_reg = &d->prg_reg[n];
- unsigned long buf = (unsigned long)d->dma.kvirt;
- int i;
- d->last_used_cmd[n] = d->nb_cmd - 1;
- for (i=0;i<d->nb_cmd;i++) {
-
- it_prg[i].begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE |
- DMA_CTL_IMMEDIATE | 8) ;
- it_prg[i].begin.address = 0;
-
- it_prg[i].begin.status = 0;
-
- it_prg[i].data[0] = cpu_to_le32(
- (IEEE1394_SPEED_100 << 16)
- | (/* tag */ 1 << 14)
- | (d->channel << 8)
- | (TCODE_ISO_DATA << 4));
- if (i==0) it_prg[i].data[0] |= cpu_to_le32(sync_tag);
- it_prg[i].data[1] = cpu_to_le32(d->packet_size << 16);
- it_prg[i].data[2] = 0;
- it_prg[i].data[3] = 0;
-
- it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST |
- DMA_CTL_BRANCH);
- it_prg[i].end.address =
- cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+i*d->packet_size) -
- (unsigned long)d->dma.kvirt));
-
- if (i<d->nb_cmd-1) {
- it_prg[i].end.control |= cpu_to_le32(d->packet_size);
- it_prg[i].begin.branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
- sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
- it_prg[i].end.branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
- sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
- } else {
- /* the last prg generates an interrupt */
- it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE |
- DMA_CTL_IRQ | d->left_size);
- /* the last prg doesn't branch */
- it_prg[i].begin.branchAddress = 0;
- it_prg[i].end.branchAddress = 0;
- }
- it_prg[i].end.status = 0;
- }
-}
-
-static void initialize_dma_it_prg_var_packet_queue(
- struct dma_iso_ctx *d, int n, unsigned int * packet_sizes,
- struct ti_ohci *ohci)
-{
- struct it_dma_prg *it_prg = d->it_prg[n];
- struct dma_prog_region *it_reg = &d->prg_reg[n];
- int i;
-
-#if 0
- if (n != -1) {
- put_timestamp(ohci, d, n);
- }
-#endif
- d->last_used_cmd[n] = d->nb_cmd - 1;
-
- for (i = 0; i < d->nb_cmd; i++) {
- unsigned int size;
- if (packet_sizes[i] > d->packet_size) {
- size = d->packet_size;
- } else {
- size = packet_sizes[i];
- }
- it_prg[i].data[1] = cpu_to_le32(size << 16);
- it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH);
-
- if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) {
- it_prg[i].end.control |= cpu_to_le32(size);
- it_prg[i].begin.branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
- sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
- it_prg[i].end.branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
- sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
- } else {
- /* the last prg generates an interrupt */
- it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE |
- DMA_CTL_IRQ | size);
- /* the last prg doesn't branch */
- it_prg[i].begin.branchAddress = 0;
- it_prg[i].end.branchAddress = 0;
- d->last_used_cmd[n] = i;
- break;
- }
- }
-}
-
-static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag,
- unsigned int syt_offset, int flags)
-{
- struct ti_ohci *ohci = (struct ti_ohci *)d->ohci;
- int i;
-
- d->flags = flags;
- d->syt_offset = (syt_offset == 0 ? 11000 : syt_offset);
-
- ohci1394_stop_context(ohci, d->ctrlClear, NULL);
-
- for (i=0;i<d->num_desc;i++)
- initialize_dma_it_prg(d, i, sync_tag);
-
- /* Set up isoRecvIntMask to generate interrupts */
- reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx);
-}
-
-static inline unsigned video1394_buffer_state(struct dma_iso_ctx *d,
- unsigned int buffer)
-{
- unsigned long flags;
- unsigned int ret;
- spin_lock_irqsave(&d->lock, flags);
- ret = d->buffer_status[buffer];
- spin_unlock_irqrestore(&d->lock, flags);
- return ret;
-}
-
-static long video1394_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct file_ctx *ctx = file->private_data;
- struct ti_ohci *ohci = ctx->ohci;
- unsigned long flags;
- void __user *argp = (void __user *)arg;
-
- switch(cmd)
- {
- case VIDEO1394_IOC_LISTEN_CHANNEL:
- case VIDEO1394_IOC_TALK_CHANNEL:
- {
- struct video1394_mmap v;
- u64 mask;
- struct dma_iso_ctx *d;
- int i;
-
- if (copy_from_user(&v, argp, sizeof(v)))
- return -EFAULT;
-
- /* if channel < 0, find lowest available one */
- if (v.channel < 0) {
- mask = (u64)0x1;
- for (i=0; ; i++) {
- if (i == ISO_CHANNELS) {
- PRINT(KERN_ERR, ohci->host->id,
- "No free channel found");
- return -EAGAIN;
- }
- if (!(ohci->ISO_channel_usage & mask)) {
- v.channel = i;
- PRINT(KERN_INFO, ohci->host->id, "Found free channel %d", i);
- break;
- }
- mask = mask << 1;
- }
- } else if (v.channel >= ISO_CHANNELS) {
- PRINT(KERN_ERR, ohci->host->id,
- "Iso channel %d out of bounds", v.channel);
- return -EINVAL;
- } else {
- mask = (u64)0x1<<v.channel;
- }
- DBGMSG(ohci->host->id, "mask: %08X%08X usage: %08X%08X\n",
- (u32)(mask>>32),(u32)(mask&0xffffffff),
- (u32)(ohci->ISO_channel_usage>>32),
- (u32)(ohci->ISO_channel_usage&0xffffffff));
- if (ohci->ISO_channel_usage & mask) {
- PRINT(KERN_ERR, ohci->host->id,
- "Channel %d is already taken", v.channel);
- return -EBUSY;
- }
-
- if (v.buf_size == 0 || v.buf_size > VIDEO1394_MAX_SIZE) {
- PRINT(KERN_ERR, ohci->host->id,
- "Invalid %d length buffer requested",v.buf_size);
- return -EINVAL;
- }
-
- if (v.nb_buffers == 0 || v.nb_buffers > VIDEO1394_MAX_SIZE) {
- PRINT(KERN_ERR, ohci->host->id,
- "Invalid %d buffers requested",v.nb_buffers);
- return -EINVAL;
- }
-
- if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) {
- PRINT(KERN_ERR, ohci->host->id,
- "%d buffers of size %d bytes is too big",
- v.nb_buffers, v.buf_size);
- return -EINVAL;
- }
-
- if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) {
- d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE,
- v.nb_buffers + 1, v.buf_size,
- v.channel, 0);
-
- if (d == NULL) {
- PRINT(KERN_ERR, ohci->host->id,
- "Couldn't allocate ir context");
- return -EAGAIN;
- }
- initialize_dma_ir_ctx(d, v.sync_tag, v.flags);
-
- ctx->current_ctx = d;
-
- v.buf_size = d->buf_size;
- list_add_tail(&d->link, &ctx->context_list);
-
- DBGMSG(ohci->host->id,
- "iso context %d listen on channel %d",
- d->ctx, v.channel);
- }
- else {
- d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT,
- v.nb_buffers + 1, v.buf_size,
- v.channel, v.packet_size);
-
- if (d == NULL) {
- PRINT(KERN_ERR, ohci->host->id,
- "Couldn't allocate it context");
- return -EAGAIN;
- }
- initialize_dma_it_ctx(d, v.sync_tag,
- v.syt_offset, v.flags);
-
- ctx->current_ctx = d;
-
- v.buf_size = d->buf_size;
-
- list_add_tail(&d->link, &ctx->context_list);
-
- DBGMSG(ohci->host->id,
- "Iso context %d talk on channel %d", d->ctx,
- v.channel);
- }
-
- if (copy_to_user(argp, &v, sizeof(v))) {
- /* FIXME : free allocated dma resources */
- return -EFAULT;
- }
-
- ohci->ISO_channel_usage |= mask;
-
- return 0;
- }
- case VIDEO1394_IOC_UNLISTEN_CHANNEL:
- case VIDEO1394_IOC_UNTALK_CHANNEL:
- {
- int channel;
- u64 mask;
- struct dma_iso_ctx *d;
-
- if (copy_from_user(&channel, argp, sizeof(int)))
- return -EFAULT;
-
- if (channel < 0 || channel >= ISO_CHANNELS) {
- PRINT(KERN_ERR, ohci->host->id,
- "Iso channel %d out of bound", channel);
- return -EINVAL;
- }
- mask = (u64)0x1<<channel;
- if (!(ohci->ISO_channel_usage & mask)) {
- PRINT(KERN_ERR, ohci->host->id,
- "Channel %d is not being used", channel);
- return -ESRCH;
- }
-
- /* Mark this channel as unused */
- ohci->ISO_channel_usage &= ~mask;
-
- if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL)
- d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel);
- else
- d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel);
-
- if (d == NULL) return -ESRCH;
- DBGMSG(ohci->host->id, "Iso context %d "
- "stop talking on channel %d", d->ctx, channel);
- free_dma_iso_ctx(d);
-
- return 0;
- }
- case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER:
- {
- struct video1394_wait v;
- struct dma_iso_ctx *d;
- int next_prg;
-
- if (unlikely(copy_from_user(&v, argp, sizeof(v))))
- return -EFAULT;
-
- d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
- if (unlikely(d == NULL))
- return -EFAULT;
-
- if (unlikely(v.buffer >= d->num_desc - 1)) {
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d out of range",v.buffer);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&d->lock,flags);
-
- if (unlikely(d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED)) {
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d is already used",v.buffer);
- spin_unlock_irqrestore(&d->lock,flags);
- return -EBUSY;
- }
-
- d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
-
- next_prg = (d->last_buffer + 1) % d->num_desc;
- if (d->last_buffer>=0)
- d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0)
- & 0xfffffff0) | 0x1);
-
- d->last_buffer = next_prg;
- reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags);
-
- d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0;
-
- spin_unlock_irqrestore(&d->lock,flags);
-
- if (!(reg_read(ohci, d->ctrlSet) & 0x8000))
- {
- DBGMSG(ohci->host->id, "Starting iso DMA ctx=%d",d->ctx);
-
- /* Tell the controller where the first program is */
- reg_write(ohci, d->cmdPtr,
- dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1);
-
- /* Run IR context */
- reg_write(ohci, d->ctrlSet, 0x8000);
- }
- else {
- /* Wake up dma context if necessary */
- if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
- DBGMSG(ohci->host->id,
- "Waking up iso dma ctx=%d", d->ctx);
- reg_write(ohci, d->ctrlSet, 0x1000);
- }
- }
- return 0;
-
- }
- case VIDEO1394_IOC_LISTEN_WAIT_BUFFER:
- case VIDEO1394_IOC_LISTEN_POLL_BUFFER:
- {
- struct video1394_wait v;
- struct dma_iso_ctx *d;
- int i = 0;
-
- if (unlikely(copy_from_user(&v, argp, sizeof(v))))
- return -EFAULT;
-
- d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
- if (unlikely(d == NULL))
- return -EFAULT;
-
- if (unlikely(v.buffer > d->num_desc - 1)) {
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d out of range",v.buffer);
- return -EINVAL;
- }
-
- /*
- * I change the way it works so that it returns
- * the last received frame.
- */
- spin_lock_irqsave(&d->lock, flags);
- switch(d->buffer_status[v.buffer]) {
- case VIDEO1394_BUFFER_READY:
- d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
- break;
- case VIDEO1394_BUFFER_QUEUED:
- if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER) {
- /* for polling, return error code EINTR */
- spin_unlock_irqrestore(&d->lock, flags);
- return -EINTR;
- }
-
- spin_unlock_irqrestore(&d->lock, flags);
- wait_event_interruptible(d->waitq,
- video1394_buffer_state(d, v.buffer) ==
- VIDEO1394_BUFFER_READY);
- if (signal_pending(current))
- return -EINTR;
- spin_lock_irqsave(&d->lock, flags);
- d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
- break;
- default:
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d is not queued",v.buffer);
- spin_unlock_irqrestore(&d->lock, flags);
- return -ESRCH;
- }
-
- /* set time of buffer */
- v.filltime = d->buffer_time[v.buffer];
-
- /*
- * Look ahead to see how many more buffers have been received
- */
- i=0;
- while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]==
- VIDEO1394_BUFFER_READY) {
- v.buffer=(v.buffer+1)%(d->num_desc - 1);
- i++;
- }
- spin_unlock_irqrestore(&d->lock, flags);
-
- v.buffer=i;
- if (unlikely(copy_to_user(argp, &v, sizeof(v))))
- return -EFAULT;
-
- return 0;
- }
- case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
- {
- struct video1394_wait v;
- unsigned int *psizes = NULL;
- struct dma_iso_ctx *d;
- int next_prg;
-
- if (copy_from_user(&v, argp, sizeof(v)))
- return -EFAULT;
-
- d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
- if (d == NULL) return -EFAULT;
-
- if (v.buffer >= d->num_desc - 1) {
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d out of range",v.buffer);
- return -EINVAL;
- }
-
- if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
- int buf_size = d->nb_cmd * sizeof(*psizes);
- struct video1394_queue_variable __user *p = argp;
- unsigned int __user *qv;
-
- if (get_user(qv, &p->packet_sizes))
- return -EFAULT;
-
- psizes = memdup_user(qv, buf_size);
- if (IS_ERR(psizes))
- return PTR_ERR(psizes);
- }
-
- spin_lock_irqsave(&d->lock,flags);
-
- /* last_buffer is last_prg */
- next_prg = (d->last_buffer + 1) % d->num_desc;
- if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) {
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d is already used",v.buffer);
- spin_unlock_irqrestore(&d->lock,flags);
- kfree(psizes);
- return -EBUSY;
- }
-
- if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
- initialize_dma_it_prg_var_packet_queue(
- d, next_prg, psizes, ohci);
- }
-
- d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
-
- if (d->last_buffer >= 0) {
- d->it_prg[d->last_buffer]
- [ d->last_used_cmd[d->last_buffer] ].end.branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
- 0) & 0xfffffff0) | 0x3);
-
- d->it_prg[d->last_buffer]
- [ d->last_used_cmd[d->last_buffer] ].begin.branchAddress =
- cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
- 0) & 0xfffffff0) | 0x3);
- d->next_buffer[d->last_buffer] = (v.buffer + 1) % (d->num_desc - 1);
- }
- d->last_buffer = next_prg;
- reprogram_dma_it_prg(d, d->last_buffer, v.buffer);
- d->next_buffer[d->last_buffer] = -1;
-
- d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0;
-
- spin_unlock_irqrestore(&d->lock,flags);
-
- if (!(reg_read(ohci, d->ctrlSet) & 0x8000))
- {
- DBGMSG(ohci->host->id, "Starting iso transmit DMA ctx=%d",
- d->ctx);
- put_timestamp(ohci, d, d->last_buffer);
- dma_region_sync_for_device(&d->dma,
- v.buffer * d->buf_size, d->buf_size);
-
- /* Tell the controller where the first program is */
- reg_write(ohci, d->cmdPtr,
- dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) | 0x3);
-
- /* Run IT context */
- reg_write(ohci, d->ctrlSet, 0x8000);
- }
- else {
- /* Wake up dma context if necessary */
- if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
- DBGMSG(ohci->host->id,
- "Waking up iso transmit dma ctx=%d",
- d->ctx);
- put_timestamp(ohci, d, d->last_buffer);
- dma_region_sync_for_device(&d->dma,
- v.buffer * d->buf_size, d->buf_size);
-
- reg_write(ohci, d->ctrlSet, 0x1000);
- }
- }
-
- kfree(psizes);
- return 0;
-
- }
- case VIDEO1394_IOC_TALK_WAIT_BUFFER:
- {
- struct video1394_wait v;
- struct dma_iso_ctx *d;
-
- if (copy_from_user(&v, argp, sizeof(v)))
- return -EFAULT;
-
- d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
- if (d == NULL) return -EFAULT;
-
- if (v.buffer >= d->num_desc - 1) {
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d out of range",v.buffer);
- return -EINVAL;
- }
-
- switch(d->buffer_status[v.buffer]) {
- case VIDEO1394_BUFFER_READY:
- d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
- return 0;
- case VIDEO1394_BUFFER_QUEUED:
- wait_event_interruptible(d->waitq,
- (d->buffer_status[v.buffer] == VIDEO1394_BUFFER_READY));
- if (signal_pending(current))
- return -EINTR;
- d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
- return 0;
- default:
- PRINT(KERN_ERR, ohci->host->id,
- "Buffer %d is not queued",v.buffer);
- return -ESRCH;
- }
- }
- default:
- return -ENOTTY;
- }
-}
-
-/*
- * This maps the vmalloced and reserved buffer to user space.
- *
- * FIXME:
- * - PAGE_READONLY should suffice!?
- * - remap_pfn_range is kind of inefficient for page by page remapping.
- * But e.g. pte_alloc() does not work in modules ... :-(
- */
-
-static int video1394_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct file_ctx *ctx = file->private_data;
-
- if (ctx->current_ctx == NULL) {
- PRINT(KERN_ERR, ctx->ohci->host->id,
- "Current iso context not set");
- return -EINVAL;
- }
-
- return dma_region_mmap(&ctx->current_ctx->dma, file, vma);
-}
-
-static unsigned int video1394_poll(struct file *file, poll_table *pt)
-{
- struct file_ctx *ctx;
- unsigned int mask = 0;
- unsigned long flags;
- struct dma_iso_ctx *d;
- int i;
-
- ctx = file->private_data;
- d = ctx->current_ctx;
- if (d == NULL) {
- PRINT(KERN_ERR, ctx->ohci->host->id,
- "Current iso context not set");
- return POLLERR;
- }
-
- poll_wait(file, &d->waitq, pt);
-
- spin_lock_irqsave(&d->lock, flags);
- for (i = 0; i < d->num_desc; i++) {
- if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) {
- mask |= POLLIN | POLLRDNORM;
- break;
- }
- }
- spin_unlock_irqrestore(&d->lock, flags);
-
- return mask;
-}
-
-static int video1394_open(struct inode *inode, struct file *file)
-{
- int i = ieee1394_file_to_instance(file);
- struct ti_ohci *ohci;
- struct file_ctx *ctx;
-
- ohci = hpsb_get_hostinfo_bykey(&video1394_highlevel, i);
- if (ohci == NULL)
- return -EIO;
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- PRINT(KERN_ERR, ohci->host->id, "Cannot malloc file_ctx");
- return -ENOMEM;
- }
-
- ctx->ohci = ohci;
- INIT_LIST_HEAD(&ctx->context_list);
- ctx->current_ctx = NULL;
- file->private_data = ctx;
-
- return nonseekable_open(inode, file);
-}
-
-static int video1394_release(struct inode *inode, struct file *file)
-{
- struct file_ctx *ctx = file->private_data;
- struct ti_ohci *ohci = ctx->ohci;
- struct list_head *lh, *next;
- u64 mask;
-
- list_for_each_safe(lh, next, &ctx->context_list) {
- struct dma_iso_ctx *d;
- d = list_entry(lh, struct dma_iso_ctx, link);
- mask = (u64) 1 << d->channel;
-
- if (!(ohci->ISO_channel_usage & mask))
- PRINT(KERN_ERR, ohci->host->id, "On release: Channel %d "
- "is not being used", d->channel);
- else
- ohci->ISO_channel_usage &= ~mask;
- DBGMSG(ohci->host->id, "On release: Iso %s context "
- "%d stop listening on channel %d",
- d->type == OHCI_ISO_RECEIVE ? "receive" : "transmit",
- d->ctx, d->channel);
- free_dma_iso_ctx(d);
- }
-
- kfree(ctx);
- file->private_data = NULL;
-
- return 0;
-}
-
-#ifdef CONFIG_COMPAT
-static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg);
-#endif
-
-static struct cdev video1394_cdev;
-static const struct file_operations video1394_fops=
-{
- .owner = THIS_MODULE,
- .unlocked_ioctl = video1394_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = video1394_compat_ioctl,
-#endif
- .poll = video1394_poll,
- .mmap = video1394_mmap,
- .open = video1394_open,
- .release = video1394_release,
- .llseek = no_llseek,
-};
-
-/*** HOTPLUG STUFF **********************************************************/
-/*
- * Export information about protocols/devices supported by this driver.
- */
-#ifdef MODULE
-static const struct ieee1394_device_id video1394_id_table[] = {
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = CAMERA_SW_VERSION_ENTRY & 0xffffff
- },
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff
- },
- {
- .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
- .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
- .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff
- },
- { }
-};
-
-MODULE_DEVICE_TABLE(ieee1394, video1394_id_table);
-#endif /* MODULE */
-
-static struct hpsb_protocol_driver video1394_driver = {
- .name = VIDEO1394_DRIVER_NAME,
-};
-
-
-static void video1394_add_host (struct hpsb_host *host)
-{
- struct ti_ohci *ohci;
- int minor;
-
- /* We only work with the OHCI-1394 driver */
- if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
- return;
-
- ohci = (struct ti_ohci *)host->hostdata;
-
- if (!hpsb_create_hostinfo(&video1394_highlevel, host, 0)) {
- PRINT(KERN_ERR, ohci->host->id, "Cannot allocate hostinfo");
- return;
- }
-
- hpsb_set_hostinfo(&video1394_highlevel, host, ohci);
- hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id);
-
- minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id;
- device_create(hpsb_protocol_class, NULL, MKDEV(IEEE1394_MAJOR, minor),
- NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id);
-}
-
-
-static void video1394_remove_host (struct hpsb_host *host)
-{
- struct ti_ohci *ohci = hpsb_get_hostinfo(&video1394_highlevel, host);
-
- if (ohci)
- device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR,
- IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id));
- return;
-}
-
-
-static struct hpsb_highlevel video1394_highlevel = {
- .name = VIDEO1394_DRIVER_NAME,
- .add_host = video1394_add_host,
- .remove_host = video1394_remove_host,
-};
-
-MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
-MODULE_DESCRIPTION("driver for digital video on OHCI board");
-MODULE_SUPPORTED_DEVICE(VIDEO1394_DRIVER_NAME);
-MODULE_LICENSE("GPL");
-
-#ifdef CONFIG_COMPAT
-
-#define VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER \
- _IOW ('#', 0x12, struct video1394_wait32)
-#define VIDEO1394_IOC32_LISTEN_WAIT_BUFFER \
- _IOWR('#', 0x13, struct video1394_wait32)
-#define VIDEO1394_IOC32_TALK_WAIT_BUFFER \
- _IOW ('#', 0x17, struct video1394_wait32)
-#define VIDEO1394_IOC32_LISTEN_POLL_BUFFER \
- _IOWR('#', 0x18, struct video1394_wait32)
-
-struct video1394_wait32 {
- u32 channel;
- u32 buffer;
- struct compat_timeval filltime;
-};
-
-static int video1394_wr_wait32(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct video1394_wait32 __user *argp = (void __user *)arg;
- struct video1394_wait32 wait32;
- struct video1394_wait wait;
- mm_segment_t old_fs;
- int ret;
-
- if (copy_from_user(&wait32, argp, sizeof(wait32)))
- return -EFAULT;
-
- wait.channel = wait32.channel;
- wait.buffer = wait32.buffer;
- wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec;
- wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec;
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- if (cmd == VIDEO1394_IOC32_LISTEN_WAIT_BUFFER)
- ret = video1394_ioctl(file,
- VIDEO1394_IOC_LISTEN_WAIT_BUFFER,
- (unsigned long) &wait);
- else
- ret = video1394_ioctl(file,
- VIDEO1394_IOC_LISTEN_POLL_BUFFER,
- (unsigned long) &wait);
- set_fs(old_fs);
-
- if (!ret) {
- wait32.channel = wait.channel;
- wait32.buffer = wait.buffer;
- wait32.filltime.tv_sec = (int)wait.filltime.tv_sec;
- wait32.filltime.tv_usec = (int)wait.filltime.tv_usec;
-
- if (copy_to_user(argp, &wait32, sizeof(wait32)))
- ret = -EFAULT;
- }
-
- return ret;
-}
-
-static int video1394_w_wait32(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct video1394_wait32 wait32;
- struct video1394_wait wait;
- mm_segment_t old_fs;
- int ret;
-
- if (copy_from_user(&wait32, (void __user *)arg, sizeof(wait32)))
- return -EFAULT;
-
- wait.channel = wait32.channel;
- wait.buffer = wait32.buffer;
- wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec;
- wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec;
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- if (cmd == VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER)
- ret = video1394_ioctl(file,
- VIDEO1394_IOC_LISTEN_QUEUE_BUFFER,
- (unsigned long) &wait);
- else
- ret = video1394_ioctl(file,
- VIDEO1394_IOC_TALK_WAIT_BUFFER,
- (unsigned long) &wait);
- set_fs(old_fs);
-
- return ret;
-}
-
-static int video1394_queue_buf32(struct file *file, unsigned int cmd, unsigned long arg)
-{
- return -EFAULT; /* ??? was there before. */
-
- return video1394_ioctl(file,
- VIDEO1394_IOC_TALK_QUEUE_BUFFER, arg);
-}
-
-static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg)
-{
- switch (cmd) {
- case VIDEO1394_IOC_LISTEN_CHANNEL:
- case VIDEO1394_IOC_UNLISTEN_CHANNEL:
- case VIDEO1394_IOC_TALK_CHANNEL:
- case VIDEO1394_IOC_UNTALK_CHANNEL:
- return video1394_ioctl(f, cmd, arg);
-
- case VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER:
- return video1394_w_wait32(f, cmd, arg);
- case VIDEO1394_IOC32_LISTEN_WAIT_BUFFER:
- return video1394_wr_wait32(f, cmd, arg);
- case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
- return video1394_queue_buf32(f, cmd, arg);
- case VIDEO1394_IOC32_TALK_WAIT_BUFFER:
- return video1394_w_wait32(f, cmd, arg);
- case VIDEO1394_IOC32_LISTEN_POLL_BUFFER:
- return video1394_wr_wait32(f, cmd, arg);
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-#endif /* CONFIG_COMPAT */
-
-static void __exit video1394_exit_module (void)
-{
- hpsb_unregister_protocol(&video1394_driver);
- hpsb_unregister_highlevel(&video1394_highlevel);
- cdev_del(&video1394_cdev);
- PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module");
-}
-
-static int __init video1394_init_module (void)
-{
- int ret;
-
- hpsb_init_highlevel(&video1394_highlevel);
-
- cdev_init(&video1394_cdev, &video1394_fops);
- video1394_cdev.owner = THIS_MODULE;
- ret = cdev_add(&video1394_cdev, IEEE1394_VIDEO1394_DEV, 16);
- if (ret) {
- PRINT_G(KERN_ERR, "video1394: unable to get minor device block");
- return ret;
- }
-
- hpsb_register_highlevel(&video1394_highlevel);
-
- ret = hpsb_register_protocol(&video1394_driver);
- if (ret) {
- PRINT_G(KERN_ERR, "video1394: failed to register protocol");
- hpsb_unregister_highlevel(&video1394_highlevel);
- cdev_del(&video1394_cdev);
- return ret;
- }
-
- PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module");
- return 0;
-}
-
-
-module_init(video1394_init_module);
-module_exit(video1394_exit_module);
diff --git a/drivers/ieee1394/video1394.h b/drivers/ieee1394/video1394.h
deleted file mode 100644
index 9a89d9cc3c85..000000000000
--- a/drivers/ieee1394/video1394.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * video1394.h - driver for OHCI 1394 boards
- * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
- * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef _VIDEO_1394_H
-#define _VIDEO_1394_H
-
-#include "ieee1394-ioctl.h"
-
-#define VIDEO1394_DRIVER_NAME "video1394"
-
-#define VIDEO1394_MAX_SIZE 0x4000000
-
-enum {
- VIDEO1394_BUFFER_FREE = 0,
- VIDEO1394_BUFFER_QUEUED,
- VIDEO1394_BUFFER_READY
-};
-
-#define VIDEO1394_SYNC_FRAMES 0x00000001
-#define VIDEO1394_INCLUDE_ISO_HEADERS 0x00000002
-#define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004
-
-struct video1394_mmap {
- int channel; /* -1 to find an open channel in LISTEN/TALK */
- unsigned int sync_tag;
- unsigned int nb_buffers;
- unsigned int buf_size;
- unsigned int packet_size; /* For VARIABLE_PACKET_SIZE:
- Maximum packet size */
- unsigned int fps;
- unsigned int syt_offset;
- unsigned int flags;
-};
-
-/* For TALK_QUEUE_BUFFER with VIDEO1394_VARIABLE_PACKET_SIZE use */
-struct video1394_queue_variable {
- unsigned int channel;
- unsigned int buffer;
- unsigned int __user * packet_sizes; /* Buffer of size:
- buf_size / packet_size */
-};
-
-struct video1394_wait {
- unsigned int channel;
- unsigned int buffer;
- struct timeval filltime; /* time of buffer full */
-};
-
-
-#endif
diff --git a/drivers/infiniband/core/agent.c b/drivers/infiniband/core/agent.c
index ae7c2880e624..91916a8d5de4 100644
--- a/drivers/infiniband/core/agent.c
+++ b/drivers/infiniband/core/agent.c
@@ -59,8 +59,8 @@ __ib_get_agent_port(struct ib_device *device, int port_num)
struct ib_agent_port_private *entry;
list_for_each_entry(entry, &ib_agent_port_list, port_list) {
- if (entry->agent[0]->device == device &&
- entry->agent[0]->port_num == port_num)
+ if (entry->agent[1]->device == device &&
+ entry->agent[1]->port_num == port_num)
return entry;
}
return NULL;
@@ -155,14 +155,16 @@ int ib_agent_port_open(struct ib_device *device, int port_num)
goto error1;
}
- /* Obtain send only MAD agent for SMI QP */
- port_priv->agent[0] = ib_register_mad_agent(device, port_num,
- IB_QPT_SMI, NULL, 0,
- &agent_send_handler,
- NULL, NULL);
- if (IS_ERR(port_priv->agent[0])) {
- ret = PTR_ERR(port_priv->agent[0]);
- goto error2;
+ if (rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND) {
+ /* Obtain send only MAD agent for SMI QP */
+ port_priv->agent[0] = ib_register_mad_agent(device, port_num,
+ IB_QPT_SMI, NULL, 0,
+ &agent_send_handler,
+ NULL, NULL);
+ if (IS_ERR(port_priv->agent[0])) {
+ ret = PTR_ERR(port_priv->agent[0]);
+ goto error2;
+ }
}
/* Obtain send only MAD agent for GSI QP */
@@ -182,7 +184,8 @@ int ib_agent_port_open(struct ib_device *device, int port_num)
return 0;
error3:
- ib_unregister_mad_agent(port_priv->agent[0]);
+ if (port_priv->agent[0])
+ ib_unregister_mad_agent(port_priv->agent[0]);
error2:
kfree(port_priv);
error1:
@@ -205,7 +208,9 @@ int ib_agent_port_close(struct ib_device *device, int port_num)
spin_unlock_irqrestore(&ib_agent_port_list_lock, flags);
ib_unregister_mad_agent(port_priv->agent[1]);
- ib_unregister_mad_agent(port_priv->agent[0]);
+ if (port_priv->agent[0])
+ ib_unregister_mad_agent(port_priv->agent[0]);
+
kfree(port_priv);
return 0;
}
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index b930b8110a63..6884da24fde1 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -59,6 +59,7 @@ MODULE_LICENSE("Dual BSD/GPL");
#define CMA_CM_RESPONSE_TIMEOUT 20
#define CMA_MAX_CM_RETRIES 15
#define CMA_CM_MRA_SETTING (IB_CM_MRA_FLAG_DELAY | 24)
+#define CMA_IBOE_PACKET_LIFETIME 18
static void cma_add_one(struct ib_device *device);
static void cma_remove_one(struct ib_device *device);
@@ -157,6 +158,7 @@ struct cma_multicast {
struct list_head list;
void *context;
struct sockaddr_storage addr;
+ struct kref mcref;
};
struct cma_work {
@@ -173,6 +175,12 @@ struct cma_ndev_work {
struct rdma_cm_event event;
};
+struct iboe_mcast_work {
+ struct work_struct work;
+ struct rdma_id_private *id;
+ struct cma_multicast *mc;
+};
+
union cma_ip_addr {
struct in6_addr ip6;
struct {
@@ -281,6 +289,8 @@ static void cma_attach_to_dev(struct rdma_id_private *id_priv,
atomic_inc(&cma_dev->refcount);
id_priv->cma_dev = cma_dev;
id_priv->id.device = cma_dev->device;
+ id_priv->id.route.addr.dev_addr.transport =
+ rdma_node_get_transport(cma_dev->device->node_type);
list_add_tail(&id_priv->list, &cma_dev->id_list);
}
@@ -290,6 +300,14 @@ static inline void cma_deref_dev(struct cma_device *cma_dev)
complete(&cma_dev->comp);
}
+static inline void release_mc(struct kref *kref)
+{
+ struct cma_multicast *mc = container_of(kref, struct cma_multicast, mcref);
+
+ kfree(mc->multicast.ib);
+ kfree(mc);
+}
+
static void cma_detach_from_dev(struct rdma_id_private *id_priv)
{
list_del(&id_priv->list);
@@ -323,22 +341,63 @@ static int cma_set_qkey(struct rdma_id_private *id_priv)
return ret;
}
+static int find_gid_port(struct ib_device *device, union ib_gid *gid, u8 port_num)
+{
+ int i;
+ int err;
+ struct ib_port_attr props;
+ union ib_gid tmp;
+
+ err = ib_query_port(device, port_num, &props);
+ if (err)
+ return 1;
+
+ for (i = 0; i < props.gid_tbl_len; ++i) {
+ err = ib_query_gid(device, port_num, i, &tmp);
+ if (err)
+ return 1;
+ if (!memcmp(&tmp, gid, sizeof tmp))
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
static int cma_acquire_dev(struct rdma_id_private *id_priv)
{
struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
struct cma_device *cma_dev;
- union ib_gid gid;
+ union ib_gid gid, iboe_gid;
int ret = -ENODEV;
+ u8 port;
+ enum rdma_link_layer dev_ll = dev_addr->dev_type == ARPHRD_INFINIBAND ?
+ IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET;
- rdma_addr_get_sgid(dev_addr, &gid);
+ iboe_addr_get_sgid(dev_addr, &iboe_gid);
+ memcpy(&gid, dev_addr->src_dev_addr +
+ rdma_addr_gid_offset(dev_addr), sizeof gid);
list_for_each_entry(cma_dev, &dev_list, list) {
- ret = ib_find_cached_gid(cma_dev->device, &gid,
- &id_priv->id.port_num, NULL);
- if (!ret) {
- cma_attach_to_dev(id_priv, cma_dev);
- break;
+ for (port = 1; port <= cma_dev->device->phys_port_cnt; ++port) {
+ if (rdma_port_get_link_layer(cma_dev->device, port) == dev_ll) {
+ if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB &&
+ rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET)
+ ret = find_gid_port(cma_dev->device, &iboe_gid, port);
+ else
+ ret = find_gid_port(cma_dev->device, &gid, port);
+
+ if (!ret) {
+ id_priv->id.port_num = port;
+ goto out;
+ } else if (ret == 1)
+ break;
+ }
}
}
+
+out:
+ if (!ret)
+ cma_attach_to_dev(id_priv, cma_dev);
+
return ret;
}
@@ -556,10 +615,16 @@ static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv,
{
struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
int ret;
+ u16 pkey;
+
+ if (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num) ==
+ IB_LINK_LAYER_INFINIBAND)
+ pkey = ib_addr_get_pkey(dev_addr);
+ else
+ pkey = 0xffff;
ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num,
- ib_addr_get_pkey(dev_addr),
- &qp_attr->pkey_index);
+ pkey, &qp_attr->pkey_index);
if (ret)
return ret;
@@ -737,8 +802,8 @@ static inline int cma_user_data_offset(enum rdma_port_space ps)
static void cma_cancel_route(struct rdma_id_private *id_priv)
{
- switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ switch (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
if (id_priv->query)
ib_sa_cancel_query(id_priv->query_id, id_priv->query);
break;
@@ -816,8 +881,17 @@ static void cma_leave_mc_groups(struct rdma_id_private *id_priv)
mc = container_of(id_priv->mc_list.next,
struct cma_multicast, list);
list_del(&mc->list);
- ib_sa_free_multicast(mc->multicast.ib);
- kfree(mc);
+ switch (rdma_port_get_link_layer(id_priv->cma_dev->device, id_priv->id.port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
+ ib_sa_free_multicast(mc->multicast.ib);
+ kfree(mc);
+ break;
+ case IB_LINK_LAYER_ETHERNET:
+ kref_put(&mc->mcref, release_mc);
+ break;
+ default:
+ break;
+ }
}
}
@@ -833,7 +907,7 @@ void rdma_destroy_id(struct rdma_cm_id *id)
mutex_lock(&lock);
if (id_priv->cma_dev) {
mutex_unlock(&lock);
- switch (rdma_node_get_transport(id->device->node_type)) {
+ switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
case RDMA_TRANSPORT_IB:
if (id_priv->cm_id.ib && !IS_ERR(id_priv->cm_id.ib))
ib_destroy_cm_id(id_priv->cm_id.ib);
@@ -1708,6 +1782,81 @@ static int cma_resolve_iw_route(struct rdma_id_private *id_priv, int timeout_ms)
return 0;
}
+static int cma_resolve_iboe_route(struct rdma_id_private *id_priv)
+{
+ struct rdma_route *route = &id_priv->id.route;
+ struct rdma_addr *addr = &route->addr;
+ struct cma_work *work;
+ int ret;
+ struct sockaddr_in *src_addr = (struct sockaddr_in *)&route->addr.src_addr;
+ struct sockaddr_in *dst_addr = (struct sockaddr_in *)&route->addr.dst_addr;
+ struct net_device *ndev = NULL;
+ u16 vid;
+
+ if (src_addr->sin_family != dst_addr->sin_family)
+ return -EINVAL;
+
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (!work)
+ return -ENOMEM;
+
+ work->id = id_priv;
+ INIT_WORK(&work->work, cma_work_handler);
+
+ route->path_rec = kzalloc(sizeof *route->path_rec, GFP_KERNEL);
+ if (!route->path_rec) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ route->num_paths = 1;
+
+ if (addr->dev_addr.bound_dev_if)
+ ndev = dev_get_by_index(&init_net, addr->dev_addr.bound_dev_if);
+ if (!ndev) {
+ ret = -ENODEV;
+ goto err2;
+ }
+
+ vid = rdma_vlan_dev_vlan_id(ndev);
+
+ iboe_mac_vlan_to_ll(&route->path_rec->sgid, addr->dev_addr.src_dev_addr, vid);
+ iboe_mac_vlan_to_ll(&route->path_rec->dgid, addr->dev_addr.dst_dev_addr, vid);
+
+ route->path_rec->hop_limit = 1;
+ route->path_rec->reversible = 1;
+ route->path_rec->pkey = cpu_to_be16(0xffff);
+ route->path_rec->mtu_selector = IB_SA_EQ;
+ route->path_rec->sl = id_priv->tos >> 5;
+
+ route->path_rec->mtu = iboe_get_mtu(ndev->mtu);
+ route->path_rec->rate_selector = IB_SA_EQ;
+ route->path_rec->rate = iboe_get_rate(ndev);
+ dev_put(ndev);
+ route->path_rec->packet_life_time_selector = IB_SA_EQ;
+ route->path_rec->packet_life_time = CMA_IBOE_PACKET_LIFETIME;
+ if (!route->path_rec->mtu) {
+ ret = -EINVAL;
+ goto err2;
+ }
+
+ work->old_state = CMA_ROUTE_QUERY;
+ work->new_state = CMA_ROUTE_RESOLVED;
+ work->event.event = RDMA_CM_EVENT_ROUTE_RESOLVED;
+ work->event.status = 0;
+
+ queue_work(cma_wq, &work->work);
+
+ return 0;
+
+err2:
+ kfree(route->path_rec);
+ route->path_rec = NULL;
+err1:
+ kfree(work);
+ return ret;
+}
+
int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms)
{
struct rdma_id_private *id_priv;
@@ -1720,7 +1869,16 @@ int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms)
atomic_inc(&id_priv->refcount);
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- ret = cma_resolve_ib_route(id_priv, timeout_ms);
+ switch (rdma_port_get_link_layer(id->device, id->port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
+ ret = cma_resolve_ib_route(id_priv, timeout_ms);
+ break;
+ case IB_LINK_LAYER_ETHERNET:
+ ret = cma_resolve_iboe_route(id_priv);
+ break;
+ default:
+ ret = -ENOSYS;
+ }
break;
case RDMA_TRANSPORT_IWARP:
ret = cma_resolve_iw_route(id_priv, timeout_ms);
@@ -1773,7 +1931,7 @@ port_found:
goto out;
id_priv->id.route.addr.dev_addr.dev_type =
- (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB) ?
+ (rdma_port_get_link_layer(cma_dev->device, p) == IB_LINK_LAYER_INFINIBAND) ?
ARPHRD_INFINIBAND : ARPHRD_ETHER;
rdma_addr_set_sgid(&id_priv->id.route.addr.dev_addr, &gid);
@@ -2758,6 +2916,102 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv,
return 0;
}
+static void iboe_mcast_work_handler(struct work_struct *work)
+{
+ struct iboe_mcast_work *mw = container_of(work, struct iboe_mcast_work, work);
+ struct cma_multicast *mc = mw->mc;
+ struct ib_sa_multicast *m = mc->multicast.ib;
+
+ mc->multicast.ib->context = mc;
+ cma_ib_mc_handler(0, m);
+ kref_put(&mc->mcref, release_mc);
+ kfree(mw);
+}
+
+static void cma_iboe_set_mgid(struct sockaddr *addr, union ib_gid *mgid)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+
+ if (cma_any_addr(addr)) {
+ memset(mgid, 0, sizeof *mgid);
+ } else if (addr->sa_family == AF_INET6) {
+ memcpy(mgid, &sin6->sin6_addr, sizeof *mgid);
+ } else {
+ mgid->raw[0] = 0xff;
+ mgid->raw[1] = 0x0e;
+ mgid->raw[2] = 0;
+ mgid->raw[3] = 0;
+ mgid->raw[4] = 0;
+ mgid->raw[5] = 0;
+ mgid->raw[6] = 0;
+ mgid->raw[7] = 0;
+ mgid->raw[8] = 0;
+ mgid->raw[9] = 0;
+ mgid->raw[10] = 0xff;
+ mgid->raw[11] = 0xff;
+ *(__be32 *)(&mgid->raw[12]) = sin->sin_addr.s_addr;
+ }
+}
+
+static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
+ struct cma_multicast *mc)
+{
+ struct iboe_mcast_work *work;
+ struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
+ int err;
+ struct sockaddr *addr = (struct sockaddr *)&mc->addr;
+ struct net_device *ndev = NULL;
+
+ if (cma_zero_addr((struct sockaddr *)&mc->addr))
+ return -EINVAL;
+
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (!work)
+ return -ENOMEM;
+
+ mc->multicast.ib = kzalloc(sizeof(struct ib_sa_multicast), GFP_KERNEL);
+ if (!mc->multicast.ib) {
+ err = -ENOMEM;
+ goto out1;
+ }
+
+ cma_iboe_set_mgid(addr, &mc->multicast.ib->rec.mgid);
+
+ mc->multicast.ib->rec.pkey = cpu_to_be16(0xffff);
+ if (id_priv->id.ps == RDMA_PS_UDP)
+ mc->multicast.ib->rec.qkey = cpu_to_be32(RDMA_UDP_QKEY);
+
+ if (dev_addr->bound_dev_if)
+ ndev = dev_get_by_index(&init_net, dev_addr->bound_dev_if);
+ if (!ndev) {
+ err = -ENODEV;
+ goto out2;
+ }
+ mc->multicast.ib->rec.rate = iboe_get_rate(ndev);
+ mc->multicast.ib->rec.hop_limit = 1;
+ mc->multicast.ib->rec.mtu = iboe_get_mtu(ndev->mtu);
+ dev_put(ndev);
+ if (!mc->multicast.ib->rec.mtu) {
+ err = -EINVAL;
+ goto out2;
+ }
+ iboe_addr_get_sgid(dev_addr, &mc->multicast.ib->rec.port_gid);
+ work->id = id_priv;
+ work->mc = mc;
+ INIT_WORK(&work->work, iboe_mcast_work_handler);
+ kref_get(&mc->mcref);
+ queue_work(cma_wq, &work->work);
+
+ return 0;
+
+out2:
+ kfree(mc->multicast.ib);
+out1:
+ kfree(work);
+ return err;
+}
+
int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr,
void *context)
{
@@ -2784,7 +3038,17 @@ int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr,
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- ret = cma_join_ib_multicast(id_priv, mc);
+ switch (rdma_port_get_link_layer(id->device, id->port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
+ ret = cma_join_ib_multicast(id_priv, mc);
+ break;
+ case IB_LINK_LAYER_ETHERNET:
+ kref_init(&mc->mcref);
+ ret = cma_iboe_join_multicast(id_priv, mc);
+ break;
+ default:
+ ret = -EINVAL;
+ }
break;
default:
ret = -ENOSYS;
@@ -2817,8 +3081,19 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr)
ib_detach_mcast(id->qp,
&mc->multicast.ib->rec.mgid,
mc->multicast.ib->rec.mlid);
- ib_sa_free_multicast(mc->multicast.ib);
- kfree(mc);
+ if (rdma_node_get_transport(id_priv->cma_dev->device->node_type) == RDMA_TRANSPORT_IB) {
+ switch (rdma_port_get_link_layer(id->device, id->port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
+ ib_sa_free_multicast(mc->multicast.ib);
+ kfree(mc);
+ break;
+ case IB_LINK_LAYER_ETHERNET:
+ kref_put(&mc->mcref, release_mc);
+ break;
+ default:
+ break;
+ }
+ }
return;
}
}
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c
index bfead5bc25f6..2a1e9ae134b4 100644
--- a/drivers/infiniband/core/iwcm.c
+++ b/drivers/infiniband/core/iwcm.c
@@ -506,6 +506,8 @@ int iw_cm_accept(struct iw_cm_id *cm_id,
qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn);
if (!qp) {
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+ clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
+ wake_up_all(&cm_id_priv->connect_wait);
return -EINVAL;
}
cm_id->device->iwcm->add_ref(qp);
@@ -565,6 +567,8 @@ int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn);
if (!qp) {
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
+ clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
+ wake_up_all(&cm_id_priv->connect_wait);
return -EINVAL;
}
cm_id->device->iwcm->add_ref(qp);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index ef1304f151dc..822cfdcd9f78 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -2598,6 +2598,9 @@ static void cleanup_recv_queue(struct ib_mad_qp_info *qp_info)
struct ib_mad_private *recv;
struct ib_mad_list_head *mad_list;
+ if (!qp_info->qp)
+ return;
+
while (!list_empty(&qp_info->recv_queue.list)) {
mad_list = list_entry(qp_info->recv_queue.list.next,
@@ -2639,6 +2642,9 @@ static int ib_mad_port_start(struct ib_mad_port_private *port_priv)
for (i = 0; i < IB_MAD_QPS_CORE; i++) {
qp = port_priv->qp_info[i].qp;
+ if (!qp)
+ continue;
+
/*
* PKey index for QP1 is irrelevant but
* one is needed for the Reset to Init transition
@@ -2680,6 +2686,9 @@ static int ib_mad_port_start(struct ib_mad_port_private *port_priv)
}
for (i = 0; i < IB_MAD_QPS_CORE; i++) {
+ if (!port_priv->qp_info[i].qp)
+ continue;
+
ret = ib_mad_post_receive_mads(&port_priv->qp_info[i], NULL);
if (ret) {
printk(KERN_ERR PFX "Couldn't post receive WRs\n");
@@ -2758,6 +2767,9 @@ error:
static void destroy_mad_qp(struct ib_mad_qp_info *qp_info)
{
+ if (!qp_info->qp)
+ return;
+
ib_destroy_qp(qp_info->qp);
kfree(qp_info->snoop_table);
}
@@ -2773,6 +2785,7 @@ static int ib_mad_port_open(struct ib_device *device,
struct ib_mad_port_private *port_priv;
unsigned long flags;
char name[sizeof "ib_mad123"];
+ int has_smi;
/* Create new device info */
port_priv = kzalloc(sizeof *port_priv, GFP_KERNEL);
@@ -2788,7 +2801,11 @@ static int ib_mad_port_open(struct ib_device *device,
init_mad_qp(port_priv, &port_priv->qp_info[0]);
init_mad_qp(port_priv, &port_priv->qp_info[1]);
- cq_size = (mad_sendq_size + mad_recvq_size) * 2;
+ cq_size = mad_sendq_size + mad_recvq_size;
+ has_smi = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND;
+ if (has_smi)
+ cq_size *= 2;
+
port_priv->cq = ib_create_cq(port_priv->device,
ib_mad_thread_completion_handler,
NULL, port_priv, cq_size, 0);
@@ -2812,9 +2829,11 @@ static int ib_mad_port_open(struct ib_device *device,
goto error5;
}
- ret = create_mad_qp(&port_priv->qp_info[0], IB_QPT_SMI);
- if (ret)
- goto error6;
+ if (has_smi) {
+ ret = create_mad_qp(&port_priv->qp_info[0], IB_QPT_SMI);
+ if (ret)
+ goto error6;
+ }
ret = create_mad_qp(&port_priv->qp_info[1], IB_QPT_GSI);
if (ret)
goto error7;
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index a519801dcfb7..68b4162fd9d2 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -774,6 +774,10 @@ static void mcast_event_handler(struct ib_event_handler *handler,
int index;
dev = container_of(handler, struct mcast_device, event_handler);
+ if (rdma_port_get_link_layer(dev->device, event->element.port_num) !=
+ IB_LINK_LAYER_INFINIBAND)
+ return;
+
index = event->element.port_num - dev->start_port;
switch (event->event) {
@@ -796,6 +800,7 @@ static void mcast_add_one(struct ib_device *device)
struct mcast_device *dev;
struct mcast_port *port;
int i;
+ int count = 0;
if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
return;
@@ -813,6 +818,9 @@ static void mcast_add_one(struct ib_device *device)
}
for (i = 0; i <= dev->end_port - dev->start_port; i++) {
+ if (rdma_port_get_link_layer(device, dev->start_port + i) !=
+ IB_LINK_LAYER_INFINIBAND)
+ continue;
port = &dev->port[i];
port->dev = dev;
port->port_num = dev->start_port + i;
@@ -820,6 +828,12 @@ static void mcast_add_one(struct ib_device *device)
port->table = RB_ROOT;
init_completion(&port->comp);
atomic_set(&port->refcount, 1);
+ ++count;
+ }
+
+ if (!count) {
+ kfree(dev);
+ return;
}
dev->device = device;
@@ -843,9 +857,12 @@ static void mcast_remove_one(struct ib_device *device)
flush_workqueue(mcast_wq);
for (i = 0; i <= dev->end_port - dev->start_port; i++) {
- port = &dev->port[i];
- deref_port(port);
- wait_for_completion(&port->comp);
+ if (rdma_port_get_link_layer(device, dev->start_port + i) ==
+ IB_LINK_LAYER_INFINIBAND) {
+ port = &dev->port[i];
+ deref_port(port);
+ wait_for_completion(&port->comp);
+ }
}
kfree(dev);
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 7e1ffd8ccd5c..91a660310b7c 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -416,6 +416,9 @@ static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event
struct ib_sa_port *port =
&sa_dev->port[event->element.port_num - sa_dev->start_port];
+ if (rdma_port_get_link_layer(handler->device, port->port_num) != IB_LINK_LAYER_INFINIBAND)
+ return;
+
spin_lock_irqsave(&port->ah_lock, flags);
if (port->sm_ah)
kref_put(&port->sm_ah->ref, free_sm_ah);
@@ -493,6 +496,7 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
{
int ret;
u16 gid_index;
+ int force_grh;
memset(ah_attr, 0, sizeof *ah_attr);
ah_attr->dlid = be16_to_cpu(rec->dlid);
@@ -502,7 +506,9 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
ah_attr->port_num = port_num;
ah_attr->static_rate = rec->rate;
- if (rec->hop_limit > 1) {
+ force_grh = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_ETHERNET;
+
+ if (rec->hop_limit > 1 || force_grh) {
ah_attr->ah_flags = IB_AH_GRH;
ah_attr->grh.dgid = rec->dgid;
@@ -1007,7 +1013,7 @@ static void ib_sa_add_one(struct ib_device *device)
e = device->phys_port_cnt;
}
- sa_dev = kmalloc(sizeof *sa_dev +
+ sa_dev = kzalloc(sizeof *sa_dev +
(e - s + 1) * sizeof (struct ib_sa_port),
GFP_KERNEL);
if (!sa_dev)
@@ -1017,9 +1023,12 @@ static void ib_sa_add_one(struct ib_device *device)
sa_dev->end_port = e;
for (i = 0; i <= e - s; ++i) {
+ spin_lock_init(&sa_dev->port[i].ah_lock);
+ if (rdma_port_get_link_layer(device, i + 1) != IB_LINK_LAYER_INFINIBAND)
+ continue;
+
sa_dev->port[i].sm_ah = NULL;
sa_dev->port[i].port_num = i + s;
- spin_lock_init(&sa_dev->port[i].ah_lock);
sa_dev->port[i].agent =
ib_register_mad_agent(device, i + s, IB_QPT_GSI,
@@ -1045,13 +1054,15 @@ static void ib_sa_add_one(struct ib_device *device)
goto err;
for (i = 0; i <= e - s; ++i)
- update_sm_ah(&sa_dev->port[i].update_task);
+ if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND)
+ update_sm_ah(&sa_dev->port[i].update_task);
return;
err:
while (--i >= 0)
- ib_unregister_mad_agent(sa_dev->port[i].agent);
+ if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND)
+ ib_unregister_mad_agent(sa_dev->port[i].agent);
kfree(sa_dev);
@@ -1071,9 +1082,12 @@ static void ib_sa_remove_one(struct ib_device *device)
flush_scheduled_work();
for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) {
- ib_unregister_mad_agent(sa_dev->port[i].agent);
- if (sa_dev->port[i].sm_ah)
- kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
+ if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND) {
+ ib_unregister_mad_agent(sa_dev->port[i].agent);
+ if (sa_dev->port[i].sm_ah)
+ kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
+ }
+
}
kfree(sa_dev);
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 3627300e2a10..9ab5df72df7b 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -222,6 +222,19 @@ static ssize_t phys_state_show(struct ib_port *p, struct port_attribute *unused,
}
}
+static ssize_t link_layer_show(struct ib_port *p, struct port_attribute *unused,
+ char *buf)
+{
+ switch (rdma_port_get_link_layer(p->ibdev, p->port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
+ return sprintf(buf, "%s\n", "InfiniBand");
+ case IB_LINK_LAYER_ETHERNET:
+ return sprintf(buf, "%s\n", "Ethernet");
+ default:
+ return sprintf(buf, "%s\n", "Unknown");
+ }
+}
+
static PORT_ATTR_RO(state);
static PORT_ATTR_RO(lid);
static PORT_ATTR_RO(lid_mask_count);
@@ -230,6 +243,7 @@ static PORT_ATTR_RO(sm_sl);
static PORT_ATTR_RO(cap_mask);
static PORT_ATTR_RO(rate);
static PORT_ATTR_RO(phys_state);
+static PORT_ATTR_RO(link_layer);
static struct attribute *port_default_attrs[] = {
&port_attr_state.attr,
@@ -240,6 +254,7 @@ static struct attribute *port_default_attrs[] = {
&port_attr_cap_mask.attr,
&port_attr_rate.attr,
&port_attr_phys_state.attr,
+ &port_attr_link_layer.attr,
NULL
};
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index ac7edc24165c..ca12acf38379 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -40,6 +40,7 @@
#include <linux/in6.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
+#include <linux/sysctl.h>
#include <rdma/rdma_user_cm.h>
#include <rdma/ib_marshall.h>
@@ -50,8 +51,24 @@ MODULE_AUTHOR("Sean Hefty");
MODULE_DESCRIPTION("RDMA Userspace Connection Manager Access");
MODULE_LICENSE("Dual BSD/GPL");
-enum {
- UCMA_MAX_BACKLOG = 128
+static unsigned int max_backlog = 1024;
+
+static struct ctl_table_header *ucma_ctl_table_hdr;
+static ctl_table ucma_ctl_table[] = {
+ {
+ .procname = "max_backlog",
+ .data = &max_backlog,
+ .maxlen = sizeof max_backlog,
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ { }
+};
+
+static struct ctl_path ucma_ctl_path[] = {
+ { .procname = "net" },
+ { .procname = "rdma_ucm" },
+ { }
};
struct ucma_file {
@@ -583,6 +600,42 @@ static void ucma_copy_ib_route(struct rdma_ucm_query_route_resp *resp,
}
}
+static void ucma_copy_iboe_route(struct rdma_ucm_query_route_resp *resp,
+ struct rdma_route *route)
+{
+ struct rdma_dev_addr *dev_addr;
+ struct net_device *dev;
+ u16 vid = 0;
+
+ resp->num_paths = route->num_paths;
+ switch (route->num_paths) {
+ case 0:
+ dev_addr = &route->addr.dev_addr;
+ dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if);
+ if (dev) {
+ vid = rdma_vlan_dev_vlan_id(dev);
+ dev_put(dev);
+ }
+
+ iboe_mac_vlan_to_ll((union ib_gid *) &resp->ib_route[0].dgid,
+ dev_addr->dst_dev_addr, vid);
+ iboe_addr_get_sgid(dev_addr,
+ (union ib_gid *) &resp->ib_route[0].sgid);
+ resp->ib_route[0].pkey = cpu_to_be16(0xffff);
+ break;
+ case 2:
+ ib_copy_path_rec_to_user(&resp->ib_route[1],
+ &route->path_rec[1]);
+ /* fall through */
+ case 1:
+ ib_copy_path_rec_to_user(&resp->ib_route[0],
+ &route->path_rec[0]);
+ break;
+ default:
+ break;
+ }
+}
+
static ssize_t ucma_query_route(struct ucma_file *file,
const char __user *inbuf,
int in_len, int out_len)
@@ -617,12 +670,17 @@ static ssize_t ucma_query_route(struct ucma_file *file,
resp.node_guid = (__force __u64) ctx->cm_id->device->node_guid;
resp.port_num = ctx->cm_id->port_num;
- switch (rdma_node_get_transport(ctx->cm_id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
- ucma_copy_ib_route(&resp, &ctx->cm_id->route);
- break;
- default:
- break;
+ if (rdma_node_get_transport(ctx->cm_id->device->node_type) == RDMA_TRANSPORT_IB) {
+ switch (rdma_port_get_link_layer(ctx->cm_id->device, ctx->cm_id->port_num)) {
+ case IB_LINK_LAYER_INFINIBAND:
+ ucma_copy_ib_route(&resp, &ctx->cm_id->route);
+ break;
+ case IB_LINK_LAYER_ETHERNET:
+ ucma_copy_iboe_route(&resp, &ctx->cm_id->route);
+ break;
+ default:
+ break;
+ }
}
out:
@@ -686,8 +744,8 @@ static ssize_t ucma_listen(struct ucma_file *file, const char __user *inbuf,
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ctx->backlog = cmd.backlog > 0 && cmd.backlog < UCMA_MAX_BACKLOG ?
- cmd.backlog : UCMA_MAX_BACKLOG;
+ ctx->backlog = cmd.backlog > 0 && cmd.backlog < max_backlog ?
+ cmd.backlog : max_backlog;
ret = rdma_listen(ctx->cm_id, ctx->backlog);
ucma_put_ctx(ctx);
return ret;
@@ -1279,16 +1337,26 @@ static int __init ucma_init(void)
ret = device_create_file(ucma_misc.this_device, &dev_attr_abi_version);
if (ret) {
printk(KERN_ERR "rdma_ucm: couldn't create abi_version attr\n");
- goto err;
+ goto err1;
+ }
+
+ ucma_ctl_table_hdr = register_sysctl_paths(ucma_ctl_path, ucma_ctl_table);
+ if (!ucma_ctl_table_hdr) {
+ printk(KERN_ERR "rdma_ucm: couldn't register sysctl paths\n");
+ ret = -ENOMEM;
+ goto err2;
}
return 0;
-err:
+err2:
+ device_remove_file(ucma_misc.this_device, &dev_attr_abi_version);
+err1:
misc_deregister(&ucma_misc);
return ret;
}
static void __exit ucma_cleanup(void)
{
+ unregister_sysctl_table(ucma_ctl_table_hdr);
device_remove_file(ucma_misc.this_device, &dev_attr_abi_version);
misc_deregister(&ucma_misc);
idr_destroy(&ctx_idr);
diff --git a/drivers/infiniband/core/ud_header.c b/drivers/infiniband/core/ud_header.c
index 650b501eb142..bb7e19280821 100644
--- a/drivers/infiniband/core/ud_header.c
+++ b/drivers/infiniband/core/ud_header.c
@@ -33,6 +33,7 @@
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/if_ether.h>
#include <rdma/ib_pack.h>
@@ -80,6 +81,40 @@ static const struct ib_field lrh_table[] = {
.size_bits = 16 }
};
+static const struct ib_field eth_table[] = {
+ { STRUCT_FIELD(eth, dmac_h),
+ .offset_words = 0,
+ .offset_bits = 0,
+ .size_bits = 32 },
+ { STRUCT_FIELD(eth, dmac_l),
+ .offset_words = 1,
+ .offset_bits = 0,
+ .size_bits = 16 },
+ { STRUCT_FIELD(eth, smac_h),
+ .offset_words = 1,
+ .offset_bits = 16,
+ .size_bits = 16 },
+ { STRUCT_FIELD(eth, smac_l),
+ .offset_words = 2,
+ .offset_bits = 0,
+ .size_bits = 32 },
+ { STRUCT_FIELD(eth, type),
+ .offset_words = 3,
+ .offset_bits = 0,
+ .size_bits = 16 }
+};
+
+static const struct ib_field vlan_table[] = {
+ { STRUCT_FIELD(vlan, tag),
+ .offset_words = 0,
+ .offset_bits = 0,
+ .size_bits = 16 },
+ { STRUCT_FIELD(vlan, type),
+ .offset_words = 0,
+ .offset_bits = 16,
+ .size_bits = 16 }
+};
+
static const struct ib_field grh_table[] = {
{ STRUCT_FIELD(grh, ip_version),
.offset_words = 0,
@@ -180,38 +215,43 @@ static const struct ib_field deth_table[] = {
/**
* ib_ud_header_init - Initialize UD header structure
* @payload_bytes:Length of packet payload
+ * @lrh_present: specify if LRH is present
+ * @eth_present: specify if Eth header is present
+ * @vlan_present: packet is tagged vlan
* @grh_present:GRH flag (if non-zero, GRH will be included)
- * @immediate_present: specify if immediate data should be used
+ * @immediate_present: specify if immediate data is present
* @header:Structure to initialize
- *
- * ib_ud_header_init() initializes the lrh.link_version, lrh.link_next_header,
- * lrh.packet_length, grh.ip_version, grh.payload_length,
- * grh.next_header, bth.opcode, bth.pad_count and
- * bth.transport_header_version fields of a &struct ib_ud_header given
- * the payload length and whether a GRH will be included.
*/
void ib_ud_header_init(int payload_bytes,
+ int lrh_present,
+ int eth_present,
+ int vlan_present,
int grh_present,
int immediate_present,
struct ib_ud_header *header)
{
- u16 packet_length;
-
memset(header, 0, sizeof *header);
- header->lrh.link_version = 0;
- header->lrh.link_next_header =
- grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL;
- packet_length = (IB_LRH_BYTES +
- IB_BTH_BYTES +
- IB_DETH_BYTES +
- payload_bytes +
- 4 + /* ICRC */
- 3) / 4; /* round up */
-
- header->grh_present = grh_present;
+ if (lrh_present) {
+ u16 packet_length;
+
+ header->lrh.link_version = 0;
+ header->lrh.link_next_header =
+ grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL;
+ packet_length = (IB_LRH_BYTES +
+ IB_BTH_BYTES +
+ IB_DETH_BYTES +
+ (grh_present ? IB_GRH_BYTES : 0) +
+ payload_bytes +
+ 4 + /* ICRC */
+ 3) / 4; /* round up */
+ header->lrh.packet_length = cpu_to_be16(packet_length);
+ }
+
+ if (vlan_present)
+ header->eth.type = cpu_to_be16(ETH_P_8021Q);
+
if (grh_present) {
- packet_length += IB_GRH_BYTES / 4;
header->grh.ip_version = 6;
header->grh.payload_length =
cpu_to_be16((IB_BTH_BYTES +
@@ -222,19 +262,52 @@ void ib_ud_header_init(int payload_bytes,
header->grh.next_header = 0x1b;
}
- header->lrh.packet_length = cpu_to_be16(packet_length);
-
- header->immediate_present = immediate_present;
if (immediate_present)
header->bth.opcode = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE;
else
header->bth.opcode = IB_OPCODE_UD_SEND_ONLY;
header->bth.pad_count = (4 - payload_bytes) & 3;
header->bth.transport_header_version = 0;
+
+ header->lrh_present = lrh_present;
+ header->eth_present = eth_present;
+ header->vlan_present = vlan_present;
+ header->grh_present = grh_present;
+ header->immediate_present = immediate_present;
}
EXPORT_SYMBOL(ib_ud_header_init);
/**
+ * ib_lrh_header_pack - Pack LRH header struct into wire format
+ * @lrh:unpacked LRH header struct
+ * @buf:Buffer to pack into
+ *
+ * ib_lrh_header_pack() packs the LRH header structure @lrh into
+ * wire format in the buffer @buf.
+ */
+int ib_lrh_header_pack(struct ib_unpacked_lrh *lrh, void *buf)
+{
+ ib_pack(lrh_table, ARRAY_SIZE(lrh_table), lrh, buf);
+ return 0;
+}
+EXPORT_SYMBOL(ib_lrh_header_pack);
+
+/**
+ * ib_lrh_header_unpack - Unpack LRH structure from wire format
+ * @lrh:unpacked LRH header struct
+ * @buf:Buffer to pack into
+ *
+ * ib_lrh_header_unpack() unpacks the LRH header structure from
+ * wire format (in buf) into @lrh.
+ */
+int ib_lrh_header_unpack(void *buf, struct ib_unpacked_lrh *lrh)
+{
+ ib_unpack(lrh_table, ARRAY_SIZE(lrh_table), buf, lrh);
+ return 0;
+}
+EXPORT_SYMBOL(ib_lrh_header_unpack);
+
+/**
* ib_ud_header_pack - Pack UD header struct into wire format
* @header:UD header struct
* @buf:Buffer to pack into
@@ -247,10 +320,21 @@ int ib_ud_header_pack(struct ib_ud_header *header,
{
int len = 0;
- ib_pack(lrh_table, ARRAY_SIZE(lrh_table),
- &header->lrh, buf);
- len += IB_LRH_BYTES;
-
+ if (header->lrh_present) {
+ ib_pack(lrh_table, ARRAY_SIZE(lrh_table),
+ &header->lrh, buf + len);
+ len += IB_LRH_BYTES;
+ }
+ if (header->eth_present) {
+ ib_pack(eth_table, ARRAY_SIZE(eth_table),
+ &header->eth, buf + len);
+ len += IB_ETH_BYTES;
+ }
+ if (header->vlan_present) {
+ ib_pack(vlan_table, ARRAY_SIZE(vlan_table),
+ &header->vlan, buf + len);
+ len += IB_VLAN_BYTES;
+ }
if (header->grh_present) {
ib_pack(grh_table, ARRAY_SIZE(grh_table),
&header->grh, buf + len);
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index 5fa856909511..cd1996d0ad08 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -1022,7 +1022,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
port->ib_dev = device;
port->port_num = port_num;
- init_MUTEX(&port->sm_sem);
+ sema_init(&port->sm_sem, 1);
mutex_init(&port->file_mutex);
INIT_LIST_HEAD(&port->file_list);
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 6fcfbeb24a23..b342248aec05 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -460,6 +460,8 @@ ssize_t ib_uverbs_query_port(struct ib_uverbs_file *file,
resp.active_width = attr.active_width;
resp.active_speed = attr.active_speed;
resp.phys_state = attr.phys_state;
+ resp.link_layer = rdma_port_get_link_layer(file->device->ib_dev,
+ cmd.port_num);
if (copy_to_user((void __user *) (unsigned long) cmd.response,
&resp, sizeof resp))
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index e0fa22238715..af7a8b08b2e9 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -94,6 +94,22 @@ rdma_node_get_transport(enum rdma_node_type node_type)
}
EXPORT_SYMBOL(rdma_node_get_transport);
+enum rdma_link_layer rdma_port_get_link_layer(struct ib_device *device, u8 port_num)
+{
+ if (device->get_link_layer)
+ return device->get_link_layer(device, port_num);
+
+ switch (rdma_node_get_transport(device->node_type)) {
+ case RDMA_TRANSPORT_IB:
+ return IB_LINK_LAYER_INFINIBAND;
+ case RDMA_TRANSPORT_IWARP:
+ return IB_LINK_LAYER_ETHERNET;
+ default:
+ return IB_LINK_LAYER_UNSPECIFIED;
+ }
+}
+EXPORT_SYMBOL(rdma_port_get_link_layer);
+
/* Protection domains */
struct ib_pd *ib_alloc_pd(struct ib_device *device)
diff --git a/drivers/infiniband/hw/amso1100/Kbuild b/drivers/infiniband/hw/amso1100/Kbuild
index 06964c4af849..950dfabcd89d 100644
--- a/drivers/infiniband/hw/amso1100/Kbuild
+++ b/drivers/infiniband/hw/amso1100/Kbuild
@@ -1,6 +1,4 @@
-ifdef CONFIG_INFINIBAND_AMSO1100_DEBUG
-EXTRA_CFLAGS += -DDEBUG
-endif
+ccflags-$(CONFIG_INFINIBAND_AMSO1100_DEBUG) := -DDEBUG
obj-$(CONFIG_INFINIBAND_AMSO1100) += iw_c2.o
diff --git a/drivers/infiniband/hw/amso1100/c2_intr.c b/drivers/infiniband/hw/amso1100/c2_intr.c
index 3b5095470cb3..0ebe4e806b86 100644
--- a/drivers/infiniband/hw/amso1100/c2_intr.c
+++ b/drivers/infiniband/hw/amso1100/c2_intr.c
@@ -62,8 +62,8 @@ void c2_rnic_interrupt(struct c2_dev *c2dev)
static void handle_mq(struct c2_dev *c2dev, u32 mq_index)
{
if (c2dev->qptr_array[mq_index] == NULL) {
- pr_debug(KERN_INFO "handle_mq: stray activity for mq_index=%d\n",
- mq_index);
+ pr_debug("handle_mq: stray activity for mq_index=%d\n",
+ mq_index);
return;
}
diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile
index 7e7b5a66f042..621619c794e5 100644
--- a/drivers/infiniband/hw/cxgb3/Makefile
+++ b/drivers/infiniband/hw/cxgb3/Makefile
@@ -1,10 +1,8 @@
-EXTRA_CFLAGS += -Idrivers/net/cxgb3
+ccflags-y := -Idrivers/net/cxgb3
obj-$(CONFIG_INFINIBAND_CXGB3) += iw_cxgb3.o
iw_cxgb3-y := iwch_cm.o iwch_ev.o iwch_cq.o iwch_qp.o iwch_mem.o \
iwch_provider.o iwch.o cxio_hal.o cxio_resource.o
-ifdef CONFIG_INFINIBAND_CXGB3_DEBUG
-EXTRA_CFLAGS += -DDEBUG
-endif
+ccflags-$(CONFIG_INFINIBAND_CXGB3_DEBUG) += -DDEBUG
diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c
index 005b7b52bc1e..09dda0b8740e 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_hal.c
+++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c
@@ -160,6 +160,7 @@ int cxio_create_cq(struct cxio_rdev *rdev_p, struct t3_cq *cq, int kernel)
struct rdma_cq_setup setup;
int size = (1UL << (cq->size_log2)) * sizeof(struct t3_cqe);
+ size += 1; /* one extra page for storing cq-in-err state */
cq->cqid = cxio_hal_get_cqid(rdev_p->rscp);
if (!cq->cqid)
return -ENOMEM;
diff --git a/drivers/infiniband/hw/cxgb3/cxio_wr.h b/drivers/infiniband/hw/cxgb3/cxio_wr.h
index e5ddb63e7d23..4bb997aa39d0 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_wr.h
+++ b/drivers/infiniband/hw/cxgb3/cxio_wr.h
@@ -728,6 +728,22 @@ struct t3_cq {
#define CQ_VLD_ENTRY(ptr,size_log2,cqe) (Q_GENBIT(ptr,size_log2) == \
CQE_GENBIT(*cqe))
+struct t3_cq_status_page {
+ u32 cq_err;
+};
+
+static inline int cxio_cq_in_error(struct t3_cq *cq)
+{
+ return ((struct t3_cq_status_page *)
+ &cq->queue[1 << cq->size_log2])->cq_err;
+}
+
+static inline void cxio_set_cq_in_error(struct t3_cq *cq)
+{
+ ((struct t3_cq_status_page *)
+ &cq->queue[1 << cq->size_log2])->cq_err = 1;
+}
+
static inline void cxio_set_wq_in_error(struct t3_wq *wq)
{
wq->queue->wq_in_err.err |= 1;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 13c88871dc3b..d02dcc6e5963 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -1093,8 +1093,8 @@ static int tx_ack(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
PDBG("%s ep %p credits %u\n", __func__, ep, credits);
if (credits == 0) {
- PDBG(KERN_ERR "%s 0 credit ack ep %p state %u\n",
- __func__, ep, state_read(&ep->com));
+ PDBG("%s 0 credit ack ep %p state %u\n",
+ __func__, ep, state_read(&ep->com));
return CPL_RET_BUF_DONE;
}
diff --git a/drivers/infiniband/hw/cxgb3/iwch_ev.c b/drivers/infiniband/hw/cxgb3/iwch_ev.c
index 6afc89e7572c..71e0d845da3d 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_ev.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_ev.c
@@ -76,6 +76,14 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp,
atomic_inc(&qhp->refcnt);
spin_unlock(&rnicp->lock);
+ if (qhp->attr.state == IWCH_QP_STATE_RTS) {
+ attrs.next_state = IWCH_QP_STATE_TERMINATE;
+ iwch_modify_qp(qhp->rhp, qhp, IWCH_QP_ATTR_NEXT_STATE,
+ &attrs, 1);
+ if (send_term)
+ iwch_post_terminate(qhp, rsp_msg);
+ }
+
event.event = ib_event;
event.device = chp->ibcq.device;
if (ib_event == IB_EVENT_CQ_ERR)
@@ -86,13 +94,7 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp,
if (qhp->ibqp.event_handler)
(*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context);
- if (qhp->attr.state == IWCH_QP_STATE_RTS) {
- attrs.next_state = IWCH_QP_STATE_TERMINATE;
- iwch_modify_qp(qhp->rhp, qhp, IWCH_QP_ATTR_NEXT_STATE,
- &attrs, 1);
- if (send_term)
- iwch_post_terminate(qhp, rsp_msg);
- }
+ (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
if (atomic_dec_and_test(&qhp->refcnt))
wake_up(&qhp->wait);
@@ -179,7 +181,6 @@ void iwch_ev_dispatch(struct cxio_rdev *rdev_p, struct sk_buff *skb)
case TPT_ERR_BOUND:
case TPT_ERR_INVALIDATE_SHARED_MR:
case TPT_ERR_INVALIDATE_MR_WITH_MW_BOUND:
- (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
post_qp_event(rnicp, chp, rsp_msg, IB_EVENT_QP_ACCESS_ERR, 1);
break;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index fca0b4b747e4..2e2741307af4 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -154,6 +154,8 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve
struct iwch_create_cq_resp uresp;
struct iwch_create_cq_req ureq;
struct iwch_ucontext *ucontext = NULL;
+ static int warned;
+ size_t resplen;
PDBG("%s ib_dev %p entries %d\n", __func__, ibdev, entries);
rhp = to_iwch_dev(ibdev);
@@ -217,15 +219,26 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve
uresp.key = ucontext->key;
ucontext->key += PAGE_SIZE;
spin_unlock(&ucontext->mmap_lock);
- if (ib_copy_to_udata(udata, &uresp, sizeof (uresp))) {
+ mm->key = uresp.key;
+ mm->addr = virt_to_phys(chp->cq.queue);
+ if (udata->outlen < sizeof uresp) {
+ if (!warned++)
+ printk(KERN_WARNING MOD "Warning - "
+ "downlevel libcxgb3 (non-fatal).\n");
+ mm->len = PAGE_ALIGN((1UL << uresp.size_log2) *
+ sizeof(struct t3_cqe));
+ resplen = sizeof(struct iwch_create_cq_resp_v0);
+ } else {
+ mm->len = PAGE_ALIGN(((1UL << uresp.size_log2) + 1) *
+ sizeof(struct t3_cqe));
+ uresp.memsize = mm->len;
+ resplen = sizeof uresp;
+ }
+ if (ib_copy_to_udata(udata, &uresp, resplen)) {
kfree(mm);
iwch_destroy_cq(&chp->ibcq);
return ERR_PTR(-EFAULT);
}
- mm->key = uresp.key;
- mm->addr = virt_to_phys(chp->cq.queue);
- mm->len = PAGE_ALIGN((1UL << uresp.size_log2) *
- sizeof (struct t3_cqe));
insert_mmap(ucontext, mm);
}
PDBG("created cqid 0x%0x chp %p size 0x%0x, dma_addr 0x%0llx\n",
@@ -1414,6 +1427,7 @@ int iwch_register_device(struct iwch_dev *dev)
dev->ibdev.post_send = iwch_post_send;
dev->ibdev.post_recv = iwch_post_receive;
dev->ibdev.get_protocol_stats = iwch_get_mib;
+ dev->ibdev.uverbs_abi_ver = IWCH_UVERBS_ABI_VERSION;
dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
if (!dev->ibdev.iwcm)
diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c
index c64d27bf2c15..0993137181d7 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -802,14 +802,12 @@ int iwch_post_terminate(struct iwch_qp *qhp, struct respQ_msg_t *rsp_msg)
/*
* Assumes qhp lock is held.
*/
-static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag)
+static void __flush_qp(struct iwch_qp *qhp, struct iwch_cq *rchp,
+ struct iwch_cq *schp, unsigned long *flag)
{
- struct iwch_cq *rchp, *schp;
int count;
int flushed;
- rchp = get_chp(qhp->rhp, qhp->attr.rcq);
- schp = get_chp(qhp->rhp, qhp->attr.scq);
PDBG("%s qhp %p rchp %p schp %p\n", __func__, qhp, rchp, schp);
/* take a ref on the qhp since we must release the lock */
@@ -847,10 +845,23 @@ static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag)
static void flush_qp(struct iwch_qp *qhp, unsigned long *flag)
{
- if (qhp->ibqp.uobject)
+ struct iwch_cq *rchp, *schp;
+
+ rchp = get_chp(qhp->rhp, qhp->attr.rcq);
+ schp = get_chp(qhp->rhp, qhp->attr.scq);
+
+ if (qhp->ibqp.uobject) {
cxio_set_wq_in_error(&qhp->wq);
- else
- __flush_qp(qhp, flag);
+ cxio_set_cq_in_error(&rchp->cq);
+ (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+ if (schp != rchp) {
+ cxio_set_cq_in_error(&schp->cq);
+ (*schp->ibcq.comp_handler)(&schp->ibcq,
+ schp->ibcq.cq_context);
+ }
+ return;
+ }
+ __flush_qp(qhp, rchp, schp, flag);
}
diff --git a/drivers/infiniband/hw/cxgb3/iwch_user.h b/drivers/infiniband/hw/cxgb3/iwch_user.h
index cb7086f558c1..a277c31fcaf7 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_user.h
+++ b/drivers/infiniband/hw/cxgb3/iwch_user.h
@@ -45,10 +45,18 @@ struct iwch_create_cq_req {
__u64 user_rptr_addr;
};
+struct iwch_create_cq_resp_v0 {
+ __u64 key;
+ __u32 cqid;
+ __u32 size_log2;
+};
+
struct iwch_create_cq_resp {
__u64 key;
__u32 cqid;
__u32 size_log2;
+ __u32 memsize;
+ __u32 reserved;
};
struct iwch_create_qp_resp {
diff --git a/drivers/infiniband/hw/cxgb4/Makefile b/drivers/infiniband/hw/cxgb4/Makefile
index e31a499f0172..cd20b1342aec 100644
--- a/drivers/infiniband/hw/cxgb4/Makefile
+++ b/drivers/infiniband/hw/cxgb4/Makefile
@@ -1,4 +1,4 @@
-EXTRA_CFLAGS += -Idrivers/net/cxgb4
+ccflags-y := -Idrivers/net/cxgb4
obj-$(CONFIG_INFINIBAND_CXGB4) += iw_cxgb4.o
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 32d352a88d50..0dc62b1438be 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -117,9 +117,9 @@ static int rcv_win = 256 * 1024;
module_param(rcv_win, int, 0644);
MODULE_PARM_DESC(rcv_win, "TCP receive window in bytes (default=256KB)");
-static int snd_win = 32 * 1024;
+static int snd_win = 128 * 1024;
module_param(snd_win, int, 0644);
-MODULE_PARM_DESC(snd_win, "TCP send window in bytes (default=32KB)");
+MODULE_PARM_DESC(snd_win, "TCP send window in bytes (default=128KB)");
static struct workqueue_struct *workq;
@@ -172,7 +172,7 @@ static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb,
error = cxgb4_l2t_send(rdev->lldi.ports[0], skb, l2e);
if (error < 0)
kfree_skb(skb);
- return error;
+ return error < 0 ? error : 0;
}
int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb)
@@ -187,7 +187,7 @@ int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb)
error = cxgb4_ofld_send(rdev->lldi.ports[0], skb);
if (error < 0)
kfree_skb(skb);
- return error;
+ return error < 0 ? error : 0;
}
static void release_tid(struct c4iw_rdev *rdev, u32 hwtid, struct sk_buff *skb)
@@ -219,12 +219,11 @@ static void set_emss(struct c4iw_ep *ep, u16 opt)
static enum c4iw_ep_state state_read(struct c4iw_ep_common *epc)
{
- unsigned long flags;
enum c4iw_ep_state state;
- spin_lock_irqsave(&epc->lock, flags);
+ mutex_lock(&epc->mutex);
state = epc->state;
- spin_unlock_irqrestore(&epc->lock, flags);
+ mutex_unlock(&epc->mutex);
return state;
}
@@ -235,12 +234,10 @@ static void __state_set(struct c4iw_ep_common *epc, enum c4iw_ep_state new)
static void state_set(struct c4iw_ep_common *epc, enum c4iw_ep_state new)
{
- unsigned long flags;
-
- spin_lock_irqsave(&epc->lock, flags);
+ mutex_lock(&epc->mutex);
PDBG("%s - %s -> %s\n", __func__, states[epc->state], states[new]);
__state_set(epc, new);
- spin_unlock_irqrestore(&epc->lock, flags);
+ mutex_unlock(&epc->mutex);
return;
}
@@ -251,8 +248,8 @@ static void *alloc_ep(int size, gfp_t gfp)
epc = kzalloc(size, gfp);
if (epc) {
kref_init(&epc->kref);
- spin_lock_init(&epc->lock);
- init_waitqueue_head(&epc->waitq);
+ mutex_init(&epc->mutex);
+ c4iw_init_wr_wait(&epc->wr_wait);
}
PDBG("%s alloc ep %p\n", __func__, epc);
return epc;
@@ -1131,7 +1128,6 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
{
struct c4iw_ep *ep;
struct cpl_abort_rpl_rss *rpl = cplhdr(skb);
- unsigned long flags;
int release = 0;
unsigned int tid = GET_TID(rpl);
struct tid_info *t = dev->rdev.lldi.tids;
@@ -1139,7 +1135,7 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
ep = lookup_tid(t, tid);
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
BUG_ON(!ep);
- spin_lock_irqsave(&ep->com.lock, flags);
+ mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
case ABORTING:
__state_set(&ep->com, DEAD);
@@ -1150,7 +1146,7 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
__func__, ep, ep->com.state);
break;
}
- spin_unlock_irqrestore(&ep->com.lock, flags);
+ mutex_unlock(&ep->com.mutex);
if (release)
release_ep_resources(ep);
@@ -1213,9 +1209,9 @@ static int pass_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
}
PDBG("%s ep %p status %d error %d\n", __func__, ep,
rpl->status, status2errno(rpl->status));
- ep->com.rpl_err = status2errno(rpl->status);
- ep->com.rpl_done = 1;
- wake_up(&ep->com.waitq);
+ ep->com.wr_wait.ret = status2errno(rpl->status);
+ ep->com.wr_wait.done = 1;
+ wake_up(&ep->com.wr_wait.wait);
return 0;
}
@@ -1249,9 +1245,9 @@ static int close_listsrv_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
struct c4iw_listen_ep *ep = lookup_stid(t, stid);
PDBG("%s ep %p\n", __func__, ep);
- ep->com.rpl_err = status2errno(rpl->status);
- ep->com.rpl_done = 1;
- wake_up(&ep->com.waitq);
+ ep->com.wr_wait.ret = status2errno(rpl->status);
+ ep->com.wr_wait.done = 1;
+ wake_up(&ep->com.wr_wait.wait);
return 0;
}
@@ -1478,7 +1474,6 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
struct cpl_peer_close *hdr = cplhdr(skb);
struct c4iw_ep *ep;
struct c4iw_qp_attributes attrs;
- unsigned long flags;
int disconnect = 1;
int release = 0;
int closing = 0;
@@ -1489,7 +1484,7 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
dst_confirm(ep->dst);
- spin_lock_irqsave(&ep->com.lock, flags);
+ mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
case MPA_REQ_WAIT:
__state_set(&ep->com, CLOSING);
@@ -1507,17 +1502,17 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
* in rdma connection migration (see c4iw_accept_cr()).
*/
__state_set(&ep->com, CLOSING);
- ep->com.rpl_done = 1;
- ep->com.rpl_err = -ECONNRESET;
+ ep->com.wr_wait.done = 1;
+ ep->com.wr_wait.ret = -ECONNRESET;
PDBG("waking up ep %p tid %u\n", ep, ep->hwtid);
- wake_up(&ep->com.waitq);
+ wake_up(&ep->com.wr_wait.wait);
break;
case MPA_REP_SENT:
__state_set(&ep->com, CLOSING);
- ep->com.rpl_done = 1;
- ep->com.rpl_err = -ECONNRESET;
+ ep->com.wr_wait.done = 1;
+ ep->com.wr_wait.ret = -ECONNRESET;
PDBG("waking up ep %p tid %u\n", ep, ep->hwtid);
- wake_up(&ep->com.waitq);
+ wake_up(&ep->com.wr_wait.wait);
break;
case FPDU_MODE:
start_ep_timer(ep);
@@ -1550,7 +1545,7 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
default:
BUG_ON(1);
}
- spin_unlock_irqrestore(&ep->com.lock, flags);
+ mutex_unlock(&ep->com.mutex);
if (closing) {
attrs.next_state = C4IW_QP_STATE_CLOSING;
c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
@@ -1581,7 +1576,6 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
struct c4iw_qp_attributes attrs;
int ret;
int release = 0;
- unsigned long flags;
struct tid_info *t = dev->rdev.lldi.tids;
unsigned int tid = GET_TID(req);
@@ -1591,9 +1585,17 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
ep->hwtid);
return 0;
}
- spin_lock_irqsave(&ep->com.lock, flags);
PDBG("%s ep %p tid %u state %u\n", __func__, ep, ep->hwtid,
ep->com.state);
+
+ /*
+ * Wake up any threads in rdma_init() or rdma_fini().
+ */
+ ep->com.wr_wait.done = 1;
+ ep->com.wr_wait.ret = -ECONNRESET;
+ wake_up(&ep->com.wr_wait.wait);
+
+ mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
case CONNECTING:
break;
@@ -1605,23 +1607,8 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
connect_reply_upcall(ep, -ECONNRESET);
break;
case MPA_REP_SENT:
- ep->com.rpl_done = 1;
- ep->com.rpl_err = -ECONNRESET;
- PDBG("waking up ep %p\n", ep);
- wake_up(&ep->com.waitq);
break;
case MPA_REQ_RCVD:
-
- /*
- * We're gonna mark this puppy DEAD, but keep
- * the reference on it until the ULP accepts or
- * rejects the CR. Also wake up anyone waiting
- * in rdma connection migration (see c4iw_accept_cr()).
- */
- ep->com.rpl_done = 1;
- ep->com.rpl_err = -ECONNRESET;
- PDBG("waking up ep %p tid %u\n", ep, ep->hwtid);
- wake_up(&ep->com.waitq);
break;
case MORIBUND:
case CLOSING:
@@ -1644,7 +1631,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
break;
case DEAD:
PDBG("%s PEER_ABORT IN DEAD STATE!!!!\n", __func__);
- spin_unlock_irqrestore(&ep->com.lock, flags);
+ mutex_unlock(&ep->com.mutex);
return 0;
default:
BUG_ON(1);
@@ -1655,7 +1642,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
__state_set(&ep->com, DEAD);
release = 1;
}
- spin_unlock_irqrestore(&ep->com.lock, flags);
+ mutex_unlock(&ep->com.mutex);
rpl_skb = get_skb(skb, sizeof(*rpl), GFP_KERNEL);
if (!rpl_skb) {
@@ -1681,7 +1668,6 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
struct c4iw_ep *ep;
struct c4iw_qp_attributes attrs;
struct cpl_close_con_rpl *rpl = cplhdr(skb);
- unsigned long flags;
int release = 0;
struct tid_info *t = dev->rdev.lldi.tids;
unsigned int tid = GET_TID(rpl);
@@ -1692,7 +1678,7 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
BUG_ON(!ep);
/* The cm_id may be null if we failed to connect */
- spin_lock_irqsave(&ep->com.lock, flags);
+ mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
case CLOSING:
__state_set(&ep->com, MORIBUND);
@@ -1717,7 +1703,7 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
BUG_ON(1);
break;
}
- spin_unlock_irqrestore(&ep->com.lock, flags);
+ mutex_unlock(&ep->com.mutex);
if (release)
release_ep_resources(ep);
return 0;
@@ -1725,23 +1711,24 @@ static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
static int terminate(struct c4iw_dev *dev, struct sk_buff *skb)
{
- struct c4iw_ep *ep;
- struct cpl_rdma_terminate *term = cplhdr(skb);
+ struct cpl_rdma_terminate *rpl = cplhdr(skb);
struct tid_info *t = dev->rdev.lldi.tids;
- unsigned int tid = GET_TID(term);
+ unsigned int tid = GET_TID(rpl);
+ struct c4iw_ep *ep;
+ struct c4iw_qp_attributes attrs;
ep = lookup_tid(t, tid);
+ BUG_ON(!ep);
- if (state_read(&ep->com) != FPDU_MODE)
- return 0;
+ if (ep->com.qp) {
+ printk(KERN_WARNING MOD "TERM received tid %u qpid %u\n", tid,
+ ep->com.qp->wq.sq.qid);
+ attrs.next_state = C4IW_QP_STATE_TERMINATE;
+ c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
+ } else
+ printk(KERN_WARNING MOD "TERM received tid %u no qp\n", tid);
- PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
- skb_pull(skb, sizeof *term);
- PDBG("%s saving %d bytes of term msg\n", __func__, skb->len);
- skb_copy_from_linear_data(skb, ep->com.qp->attr.terminate_buffer,
- skb->len);
- ep->com.qp->attr.terminate_msg_len = skb->len;
- ep->com.qp->attr.is_terminate_local = 0;
return 0;
}
@@ -1762,8 +1749,8 @@ static int fw4_ack(struct c4iw_dev *dev, struct sk_buff *skb)
ep = lookup_tid(t, tid);
PDBG("%s ep %p tid %u credits %u\n", __func__, ep, ep->hwtid, credits);
if (credits == 0) {
- PDBG(KERN_ERR "%s 0 credit ack ep %p tid %u state %u\n",
- __func__, ep, ep->hwtid, state_read(&ep->com));
+ PDBG("%s 0 credit ack ep %p tid %u state %u\n",
+ __func__, ep, ep->hwtid, state_read(&ep->com));
return 0;
}
@@ -2042,6 +2029,7 @@ int c4iw_create_listen(struct iw_cm_id *cm_id, int backlog)
}
state_set(&ep->com, LISTEN);
+ c4iw_init_wr_wait(&ep->com.wr_wait);
err = cxgb4_create_server(ep->com.dev->rdev.lldi.ports[0], ep->stid,
ep->com.local_addr.sin_addr.s_addr,
ep->com.local_addr.sin_port,
@@ -2050,15 +2038,8 @@ int c4iw_create_listen(struct iw_cm_id *cm_id, int backlog)
goto fail3;
/* wait for pass_open_rpl */
- wait_event_timeout(ep->com.waitq, ep->com.rpl_done, C4IW_WR_TO);
- if (ep->com.rpl_done)
- err = ep->com.rpl_err;
- else {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(ep->com.dev->rdev.lldi.pdev));
- ep->com.dev->rdev.flags = T4_FATAL_ERROR;
- err = -EIO;
- }
+ err = c4iw_wait_for_reply(&ep->com.dev->rdev, &ep->com.wr_wait, 0, 0,
+ __func__);
if (!err) {
cm_id->provider_data = ep;
goto out;
@@ -2082,20 +2063,12 @@ int c4iw_destroy_listen(struct iw_cm_id *cm_id)
might_sleep();
state_set(&ep->com, DEAD);
- ep->com.rpl_done = 0;
- ep->com.rpl_err = 0;
+ c4iw_init_wr_wait(&ep->com.wr_wait);
err = listen_stop(ep);
if (err)
goto done;
- wait_event_timeout(ep->com.waitq, ep->com.rpl_done, C4IW_WR_TO);
- if (ep->com.rpl_done)
- err = ep->com.rpl_err;
- else {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(ep->com.dev->rdev.lldi.pdev));
- ep->com.dev->rdev.flags = T4_FATAL_ERROR;
- err = -EIO;
- }
+ err = c4iw_wait_for_reply(&ep->com.dev->rdev, &ep->com.wr_wait, 0, 0,
+ __func__);
cxgb4_free_stid(ep->com.dev->rdev.lldi.tids, ep->stid, PF_INET);
done:
cm_id->rem_ref(cm_id);
@@ -2106,12 +2079,11 @@ done:
int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
{
int ret = 0;
- unsigned long flags;
int close = 0;
int fatal = 0;
struct c4iw_rdev *rdev;
- spin_lock_irqsave(&ep->com.lock, flags);
+ mutex_lock(&ep->com.mutex);
PDBG("%s ep %p state %s, abrupt %d\n", __func__, ep,
states[ep->com.state], abrupt);
@@ -2158,7 +2130,7 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
break;
}
- spin_unlock_irqrestore(&ep->com.lock, flags);
+ mutex_unlock(&ep->com.mutex);
if (close) {
if (abrupt)
ret = abort_connection(ep, NULL, gfp);
@@ -2172,6 +2144,13 @@ int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp)
return ret;
}
+static int async_event(struct c4iw_dev *dev, struct sk_buff *skb)
+{
+ struct cpl_fw6_msg *rpl = cplhdr(skb);
+ c4iw_ev_dispatch(dev, (struct t4_cqe *)&rpl->data[0]);
+ return 0;
+}
+
/*
* These are the real handlers that are called from a
* work queue.
@@ -2190,7 +2169,8 @@ static c4iw_handler_func work_handlers[NUM_CPL_CMDS] = {
[CPL_ABORT_REQ_RSS] = peer_abort,
[CPL_CLOSE_CON_RPL] = close_con_rpl,
[CPL_RDMA_TERMINATE] = terminate,
- [CPL_FW4_ACK] = fw4_ack
+ [CPL_FW4_ACK] = fw4_ack,
+ [CPL_FW6_MSG] = async_event
};
static void process_timeout(struct c4iw_ep *ep)
@@ -2198,7 +2178,7 @@ static void process_timeout(struct c4iw_ep *ep)
struct c4iw_qp_attributes attrs;
int abort = 1;
- spin_lock_irq(&ep->com.lock);
+ mutex_lock(&ep->com.mutex);
PDBG("%s ep %p tid %u state %d\n", __func__, ep, ep->hwtid,
ep->com.state);
switch (ep->com.state) {
@@ -2225,7 +2205,7 @@ static void process_timeout(struct c4iw_ep *ep)
WARN_ON(1);
abort = 0;
}
- spin_unlock_irq(&ep->com.lock);
+ mutex_unlock(&ep->com.mutex);
if (abort)
abort_connection(ep, NULL, GFP_KERNEL);
c4iw_put_ep(&ep->com);
@@ -2309,6 +2289,7 @@ static int set_tcb_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
printk(KERN_ERR MOD "Unexpected SET_TCB_RPL status %u "
"for tid %u\n", rpl->status, GET_TID(rpl));
}
+ kfree_skb(skb);
return 0;
}
@@ -2323,20 +2304,25 @@ static int fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb)
switch (rpl->type) {
case 1:
ret = (int)((be64_to_cpu(rpl->data[0]) >> 8) & 0xff);
- wr_waitp = (__force struct c4iw_wr_wait *)rpl->data[1];
+ wr_waitp = (struct c4iw_wr_wait *)(__force unsigned long) rpl->data[1];
PDBG("%s wr_waitp %p ret %u\n", __func__, wr_waitp, ret);
if (wr_waitp) {
- wr_waitp->ret = ret;
+ if (ret)
+ wr_waitp->ret = -ret;
+ else
+ wr_waitp->ret = 0;
wr_waitp->done = 1;
wake_up(&wr_waitp->wait);
}
+ kfree_skb(skb);
break;
case 2:
- c4iw_ev_dispatch(dev, (struct t4_cqe *)&rpl->data[0]);
+ sched(dev, skb);
break;
default:
printk(KERN_ERR MOD "%s unexpected fw6 msg type %u\n", __func__,
rpl->type);
+ kfree_skb(skb);
break;
}
return 0;
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index b3daf39eed4a..8d8f8add6fcd 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -55,7 +55,7 @@ static int destroy_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
V_FW_RI_RES_WR_NRES(1) |
FW_WR_COMPL(1));
res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16));
- res_wr->cookie = (u64)&wr_wait;
+ res_wr->cookie = (unsigned long) &wr_wait;
res = res_wr->res;
res->u.cq.restype = FW_RI_RES_TYPE_CQ;
res->u.cq.op = FW_RI_RES_OP_RESET;
@@ -64,14 +64,7 @@ static int destroy_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
c4iw_init_wr_wait(&wr_wait);
ret = c4iw_ofld_send(rdev, skb);
if (!ret) {
- wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO);
- if (!wr_wait.done) {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(rdev->lldi.pdev));
- rdev->flags = T4_FATAL_ERROR;
- ret = -EIO;
- } else
- ret = wr_wait.ret;
+ ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__);
}
kfree(cq->sw_queue);
@@ -132,7 +125,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
V_FW_RI_RES_WR_NRES(1) |
FW_WR_COMPL(1));
res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16));
- res_wr->cookie = (u64)&wr_wait;
+ res_wr->cookie = (unsigned long) &wr_wait;
res = res_wr->res;
res->u.cq.restype = FW_RI_RES_TYPE_CQ;
res->u.cq.op = FW_RI_RES_OP_WRITE;
@@ -157,14 +150,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
if (ret)
goto err4;
PDBG("%s wait_event wr_wait %p\n", __func__, &wr_wait);
- wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO);
- if (!wr_wait.done) {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(rdev->lldi.pdev));
- rdev->flags = T4_FATAL_ERROR;
- ret = -EIO;
- } else
- ret = wr_wait.ret;
+ ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__);
if (ret)
goto err4;
@@ -476,6 +462,11 @@ static int poll_cq(struct t4_wq *wq, struct t4_cq *cq, struct t4_cqe *cqe,
goto proc_cqe;
}
+ if (CQE_OPCODE(hw_cqe) == FW_RI_TERMINATE) {
+ ret = -EAGAIN;
+ goto skip_cqe;
+ }
+
/*
* RECV completion.
*/
@@ -696,6 +687,7 @@ static int c4iw_poll_cq_one(struct c4iw_cq *chp, struct ib_wc *wc)
case T4_ERR_MSN_RANGE:
case T4_ERR_IRD_OVERFLOW:
case T4_ERR_OPCODE:
+ case T4_ERR_INTERNAL_ERR:
wc->status = IB_WC_FATAL_ERR;
break;
case T4_ERR_SWFLUSH:
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 9bbf491d5d9e..54fbc1118abe 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -49,29 +49,33 @@ static DEFINE_MUTEX(dev_mutex);
static struct dentry *c4iw_debugfs_root;
-struct debugfs_qp_data {
+struct c4iw_debugfs_data {
struct c4iw_dev *devp;
char *buf;
int bufsize;
int pos;
};
-static int count_qps(int id, void *p, void *data)
+static int count_idrs(int id, void *p, void *data)
{
- struct c4iw_qp *qp = p;
int *countp = data;
- if (id != qp->wq.sq.qid)
- return 0;
-
*countp = *countp + 1;
return 0;
}
-static int dump_qps(int id, void *p, void *data)
+static ssize_t debugfs_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct c4iw_debugfs_data *d = file->private_data;
+
+ return simple_read_from_buffer(buf, count, ppos, d->buf, d->pos);
+}
+
+static int dump_qp(int id, void *p, void *data)
{
struct c4iw_qp *qp = p;
- struct debugfs_qp_data *qpd = data;
+ struct c4iw_debugfs_data *qpd = data;
int space;
int cc;
@@ -101,7 +105,7 @@ static int dump_qps(int id, void *p, void *data)
static int qp_release(struct inode *inode, struct file *file)
{
- struct debugfs_qp_data *qpd = file->private_data;
+ struct c4iw_debugfs_data *qpd = file->private_data;
if (!qpd) {
printk(KERN_INFO "%s null qpd?\n", __func__);
return 0;
@@ -113,7 +117,7 @@ static int qp_release(struct inode *inode, struct file *file)
static int qp_open(struct inode *inode, struct file *file)
{
- struct debugfs_qp_data *qpd;
+ struct c4iw_debugfs_data *qpd;
int ret = 0;
int count = 1;
@@ -126,7 +130,7 @@ static int qp_open(struct inode *inode, struct file *file)
qpd->pos = 0;
spin_lock_irq(&qpd->devp->lock);
- idr_for_each(&qpd->devp->qpidr, count_qps, &count);
+ idr_for_each(&qpd->devp->qpidr, count_idrs, &count);
spin_unlock_irq(&qpd->devp->lock);
qpd->bufsize = count * 128;
@@ -137,7 +141,7 @@ static int qp_open(struct inode *inode, struct file *file)
}
spin_lock_irq(&qpd->devp->lock);
- idr_for_each(&qpd->devp->qpidr, dump_qps, qpd);
+ idr_for_each(&qpd->devp->qpidr, dump_qp, qpd);
spin_unlock_irq(&qpd->devp->lock);
qpd->buf[qpd->pos++] = 0;
@@ -149,43 +153,86 @@ out:
return ret;
}
-static ssize_t qp_read(struct file *file, char __user *buf, size_t count,
- loff_t *ppos)
+static const struct file_operations qp_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = qp_open,
+ .release = qp_release,
+ .read = debugfs_read,
+ .llseek = default_llseek,
+};
+
+static int dump_stag(int id, void *p, void *data)
{
- struct debugfs_qp_data *qpd = file->private_data;
- loff_t pos = *ppos;
- loff_t avail = qpd->pos;
+ struct c4iw_debugfs_data *stagd = data;
+ int space;
+ int cc;
- if (pos < 0)
- return -EINVAL;
- if (pos >= avail)
+ space = stagd->bufsize - stagd->pos - 1;
+ if (space == 0)
+ return 1;
+
+ cc = snprintf(stagd->buf + stagd->pos, space, "0x%x\n", id<<8);
+ if (cc < space)
+ stagd->pos += cc;
+ return 0;
+}
+
+static int stag_release(struct inode *inode, struct file *file)
+{
+ struct c4iw_debugfs_data *stagd = file->private_data;
+ if (!stagd) {
+ printk(KERN_INFO "%s null stagd?\n", __func__);
return 0;
- if (count > avail - pos)
- count = avail - pos;
+ }
+ kfree(stagd->buf);
+ kfree(stagd);
+ return 0;
+}
- while (count) {
- size_t len = 0;
+static int stag_open(struct inode *inode, struct file *file)
+{
+ struct c4iw_debugfs_data *stagd;
+ int ret = 0;
+ int count = 1;
- len = min((int)count, (int)qpd->pos - (int)pos);
- if (copy_to_user(buf, qpd->buf + pos, len))
- return -EFAULT;
- if (len == 0)
- return -EINVAL;
+ stagd = kmalloc(sizeof *stagd, GFP_KERNEL);
+ if (!stagd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ stagd->devp = inode->i_private;
+ stagd->pos = 0;
+
+ spin_lock_irq(&stagd->devp->lock);
+ idr_for_each(&stagd->devp->mmidr, count_idrs, &count);
+ spin_unlock_irq(&stagd->devp->lock);
- buf += len;
- pos += len;
- count -= len;
+ stagd->bufsize = count * sizeof("0x12345678\n");
+ stagd->buf = kmalloc(stagd->bufsize, GFP_KERNEL);
+ if (!stagd->buf) {
+ ret = -ENOMEM;
+ goto err1;
}
- count = pos - *ppos;
- *ppos = pos;
- return count;
+
+ spin_lock_irq(&stagd->devp->lock);
+ idr_for_each(&stagd->devp->mmidr, dump_stag, stagd);
+ spin_unlock_irq(&stagd->devp->lock);
+
+ stagd->buf[stagd->pos++] = 0;
+ file->private_data = stagd;
+ goto out;
+err1:
+ kfree(stagd);
+out:
+ return ret;
}
-static const struct file_operations qp_debugfs_fops = {
+static const struct file_operations stag_debugfs_fops = {
.owner = THIS_MODULE,
- .open = qp_open,
- .release = qp_release,
- .read = qp_read,
+ .open = stag_open,
+ .release = stag_release,
+ .read = debugfs_read,
+ .llseek = default_llseek,
};
static int setup_debugfs(struct c4iw_dev *devp)
@@ -199,6 +246,11 @@ static int setup_debugfs(struct c4iw_dev *devp)
(void *)devp, &qp_debugfs_fops);
if (de && de->d_inode)
de->d_inode->i_size = 4096;
+
+ de = debugfs_create_file("stags", S_IWUSR, devp->debugfs_root,
+ (void *)devp, &stag_debugfs_fops);
+ if (de && de->d_inode)
+ de->d_inode->i_size = 4096;
return 0;
}
@@ -290,7 +342,14 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
printk(KERN_ERR MOD "error %d initializing rqt pool\n", err);
goto err3;
}
+ err = c4iw_ocqp_pool_create(rdev);
+ if (err) {
+ printk(KERN_ERR MOD "error %d initializing ocqp pool\n", err);
+ goto err4;
+ }
return 0;
+err4:
+ c4iw_rqtpool_destroy(rdev);
err3:
c4iw_pblpool_destroy(rdev);
err2:
@@ -317,6 +376,7 @@ static void c4iw_remove(struct c4iw_dev *dev)
idr_destroy(&dev->cqidr);
idr_destroy(&dev->qpidr);
idr_destroy(&dev->mmidr);
+ iounmap(dev->rdev.oc_mw_kva);
ib_dealloc_device(&dev->ibdev);
}
@@ -332,6 +392,17 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
}
devp->rdev.lldi = *infop;
+ devp->rdev.oc_mw_pa = pci_resource_start(devp->rdev.lldi.pdev, 2) +
+ (pci_resource_len(devp->rdev.lldi.pdev, 2) -
+ roundup_pow_of_two(devp->rdev.lldi.vr->ocq.size));
+ devp->rdev.oc_mw_kva = ioremap_wc(devp->rdev.oc_mw_pa,
+ devp->rdev.lldi.vr->ocq.size);
+
+ printk(KERN_INFO MOD "ocq memory: "
+ "hw_start 0x%x size %u mw_pa 0x%lx mw_kva %p\n",
+ devp->rdev.lldi.vr->ocq.start, devp->rdev.lldi.vr->ocq.size,
+ devp->rdev.oc_mw_pa, devp->rdev.oc_mw_kva);
+
mutex_lock(&dev_mutex);
ret = c4iw_rdev_open(&devp->rdev);
@@ -383,46 +454,6 @@ out:
return dev;
}
-static struct sk_buff *t4_pktgl_to_skb(const struct pkt_gl *gl,
- unsigned int skb_len,
- unsigned int pull_len)
-{
- struct sk_buff *skb;
- struct skb_shared_info *ssi;
-
- if (gl->tot_len <= 512) {
- skb = alloc_skb(gl->tot_len, GFP_ATOMIC);
- if (unlikely(!skb))
- goto out;
- __skb_put(skb, gl->tot_len);
- skb_copy_to_linear_data(skb, gl->va, gl->tot_len);
- } else {
- skb = alloc_skb(skb_len, GFP_ATOMIC);
- if (unlikely(!skb))
- goto out;
- __skb_put(skb, pull_len);
- skb_copy_to_linear_data(skb, gl->va, pull_len);
-
- ssi = skb_shinfo(skb);
- ssi->frags[0].page = gl->frags[0].page;
- ssi->frags[0].page_offset = gl->frags[0].page_offset + pull_len;
- ssi->frags[0].size = gl->frags[0].size - pull_len;
- if (gl->nfrags > 1)
- memcpy(&ssi->frags[1], &gl->frags[1],
- (gl->nfrags - 1) * sizeof(skb_frag_t));
- ssi->nr_frags = gl->nfrags;
-
- skb->len = gl->tot_len;
- skb->data_len = skb->len - pull_len;
- skb->truesize += skb->data_len;
-
- /* Get a reference for the last page, we don't own it */
- get_page(gl->frags[gl->nfrags - 1].page);
- }
-out:
- return skb;
-}
-
static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp,
const struct pkt_gl *gl)
{
@@ -447,7 +478,7 @@ static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp,
c4iw_ev_handler(dev, qid);
return 0;
} else {
- skb = t4_pktgl_to_skb(gl, 128, 128);
+ skb = cxgb4_pktgl_to_skb(gl, 128, 128);
if (unlikely(!skb))
goto nomem;
}
diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c
index 491e76a0327f..c13041a0aeba 100644
--- a/drivers/infiniband/hw/cxgb4/ev.c
+++ b/drivers/infiniband/hw/cxgb4/ev.c
@@ -60,7 +60,7 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
if (qhp->attr.state == C4IW_QP_STATE_RTS) {
attrs.next_state = C4IW_QP_STATE_TERMINATE;
c4iw_modify_qp(qhp->rhp, qhp, C4IW_QP_ATTR_NEXT_STATE,
- &attrs, 1);
+ &attrs, 0);
}
event.event = ib_event;
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index ed459b8f800f..16032cdb4337 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -46,6 +46,7 @@
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/kfifo.h>
+#include <linux/mutex.h>
#include <asm/byteorder.h>
@@ -79,21 +80,6 @@ static inline void *cplhdr(struct sk_buff *skb)
return skb->data;
}
-#define C4IW_WR_TO (10*HZ)
-
-struct c4iw_wr_wait {
- wait_queue_head_t wait;
- int done;
- int ret;
-};
-
-static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp)
-{
- wr_waitp->ret = 0;
- wr_waitp->done = 0;
- init_waitqueue_head(&wr_waitp->wait);
-}
-
struct c4iw_resource {
struct kfifo tpt_fifo;
spinlock_t tpt_fifo_lock;
@@ -127,8 +113,11 @@ struct c4iw_rdev {
struct c4iw_dev_ucontext uctx;
struct gen_pool *pbl_pool;
struct gen_pool *rqt_pool;
+ struct gen_pool *ocqp_pool;
u32 flags;
struct cxgb4_lld_info lldi;
+ unsigned long oc_mw_pa;
+ void __iomem *oc_mw_kva;
};
static inline int c4iw_fatal_error(struct c4iw_rdev *rdev)
@@ -141,6 +130,44 @@ static inline int c4iw_num_stags(struct c4iw_rdev *rdev)
return min((int)T4_MAX_NUM_STAG, (int)(rdev->lldi.vr->stag.size >> 5));
}
+#define C4IW_WR_TO (10*HZ)
+
+struct c4iw_wr_wait {
+ wait_queue_head_t wait;
+ int done;
+ int ret;
+};
+
+static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp)
+{
+ wr_waitp->ret = 0;
+ wr_waitp->done = 0;
+ init_waitqueue_head(&wr_waitp->wait);
+}
+
+static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev,
+ struct c4iw_wr_wait *wr_waitp,
+ u32 hwtid, u32 qpid,
+ const char *func)
+{
+ unsigned to = C4IW_WR_TO;
+ do {
+
+ wait_event_timeout(wr_waitp->wait, wr_waitp->done, to);
+ if (!wr_waitp->done) {
+ printk(KERN_ERR MOD "%s - Device %s not responding - "
+ "tid %u qpid %u\n", func,
+ pci_name(rdev->lldi.pdev), hwtid, qpid);
+ to = to << 2;
+ }
+ } while (!wr_waitp->done);
+ if (wr_waitp->ret)
+ printk(KERN_WARNING MOD "%s: FW reply %d tid %u qpid %u\n",
+ pci_name(rdev->lldi.pdev), wr_waitp->ret, hwtid, qpid);
+ return wr_waitp->ret;
+}
+
+
struct c4iw_dev {
struct ib_device ibdev;
struct c4iw_rdev rdev;
@@ -327,6 +354,7 @@ struct c4iw_qp {
struct c4iw_qp_attributes attr;
struct t4_wq wq;
spinlock_t lock;
+ struct mutex mutex;
atomic_t refcnt;
wait_queue_head_t wait;
struct timer_list timer;
@@ -579,12 +607,10 @@ struct c4iw_ep_common {
struct c4iw_dev *dev;
enum c4iw_ep_state state;
struct kref kref;
- spinlock_t lock;
+ struct mutex mutex;
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
- wait_queue_head_t waitq;
- int rpl_done;
- int rpl_err;
+ struct c4iw_wr_wait wr_wait;
unsigned long flags;
};
@@ -654,8 +680,10 @@ int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid);
int c4iw_init_ctrl_qp(struct c4iw_rdev *rdev);
int c4iw_pblpool_create(struct c4iw_rdev *rdev);
int c4iw_rqtpool_create(struct c4iw_rdev *rdev);
+int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev);
void c4iw_pblpool_destroy(struct c4iw_rdev *rdev);
void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev);
+void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev);
void c4iw_destroy_resource(struct c4iw_resource *rscp);
int c4iw_destroy_ctrl_qp(struct c4iw_rdev *rdev);
int c4iw_register_device(struct c4iw_dev *dev);
@@ -721,6 +749,8 @@ u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size);
void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size);
u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size);
void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size);
+u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size);
+void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size);
int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb);
void c4iw_flush_hw_cq(struct t4_cq *cq);
void c4iw_count_rcqes(struct t4_cq *cq, struct t4_wq *wq, int *count);
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 269373a62f22..273ffe49525a 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -71,7 +71,7 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
if (i == (num_wqe-1)) {
req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR) |
FW_WR_COMPL(1));
- req->wr.wr_lo = (__force __be64)&wr_wait;
+ req->wr.wr_lo = (__force __be64)(unsigned long) &wr_wait;
} else
req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR));
req->wr.wr_mid = cpu_to_be32(
@@ -103,14 +103,7 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
len -= C4IW_MAX_INLINE_SIZE;
}
- wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO);
- if (!wr_wait.done) {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(rdev->lldi.pdev));
- rdev->flags = T4_FATAL_ERROR;
- ret = -EIO;
- } else
- ret = wr_wait.ret;
+ ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, 0, __func__);
return ret;
}
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index 8f645c83a125..f66dd8bf5128 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -54,9 +54,9 @@
#include "iw_cxgb4.h"
-static int fastreg_support;
+static int fastreg_support = 1;
module_param(fastreg_support, int, 0644);
-MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=0)");
+MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=1)");
static int c4iw_modify_port(struct ib_device *ibdev,
u8 port, int port_modify_mask,
@@ -149,19 +149,28 @@ static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
addr = mm->addr;
kfree(mm);
- if ((addr >= pci_resource_start(rdev->lldi.pdev, 2)) &&
- (addr < (pci_resource_start(rdev->lldi.pdev, 2) +
- pci_resource_len(rdev->lldi.pdev, 2)))) {
+ if ((addr >= pci_resource_start(rdev->lldi.pdev, 0)) &&
+ (addr < (pci_resource_start(rdev->lldi.pdev, 0) +
+ pci_resource_len(rdev->lldi.pdev, 0)))) {
/*
- * Map T4 DB register.
+ * MA_SYNC register...
*/
- if (vma->vm_flags & VM_READ)
- return -EPERM;
-
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND;
- vma->vm_flags &= ~VM_MAYREAD;
+ ret = io_remap_pfn_range(vma, vma->vm_start,
+ addr >> PAGE_SHIFT,
+ len, vma->vm_page_prot);
+ } else if ((addr >= pci_resource_start(rdev->lldi.pdev, 2)) &&
+ (addr < (pci_resource_start(rdev->lldi.pdev, 2) +
+ pci_resource_len(rdev->lldi.pdev, 2)))) {
+
+ /*
+ * Map user DB or OCQP memory...
+ */
+ if (addr >= rdev->oc_mw_pa)
+ vma->vm_page_prot = t4_pgprot_wc(vma->vm_page_prot);
+ else
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
ret = io_remap_pfn_range(vma, vma->vm_start,
addr >> PAGE_SHIFT,
len, vma->vm_page_prot);
@@ -382,7 +391,17 @@ static ssize_t show_board(struct device *dev, struct device_attribute *attr,
static int c4iw_get_mib(struct ib_device *ibdev,
union rdma_protocol_stats *stats)
{
- return -ENOSYS;
+ struct tp_tcp_stats v4, v6;
+ struct c4iw_dev *c4iw_dev = to_c4iw_dev(ibdev);
+
+ cxgb4_get_tcp_stats(c4iw_dev->rdev.lldi.pdev, &v4, &v6);
+ memset(stats, 0, sizeof *stats);
+ stats->iw.tcpInSegs = v4.tcpInSegs + v6.tcpInSegs;
+ stats->iw.tcpOutSegs = v4.tcpOutSegs + v6.tcpOutSegs;
+ stats->iw.tcpRetransSegs = v4.tcpRetransSegs + v6.tcpRetransSegs;
+ stats->iw.tcpOutRsts = v4.tcpOutRsts + v6.tcpOutSegs;
+
+ return 0;
}
static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL);
@@ -472,6 +491,7 @@ int c4iw_register_device(struct c4iw_dev *dev)
dev->ibdev.post_send = c4iw_post_send;
dev->ibdev.post_recv = c4iw_post_receive;
dev->ibdev.get_protocol_stats = c4iw_get_mib;
+ dev->ibdev.uverbs_abi_ver = C4IW_UVERBS_ABI_VERSION;
dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
if (!dev->ibdev.iwcm)
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 93f6e5bf0ec5..057cb2505ea1 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -31,6 +31,63 @@
*/
#include "iw_cxgb4.h"
+static int ocqp_support;
+module_param(ocqp_support, int, 0644);
+MODULE_PARM_DESC(ocqp_support, "Support on-chip SQs (default=0)");
+
+static void set_state(struct c4iw_qp *qhp, enum c4iw_qp_state state)
+{
+ unsigned long flag;
+ spin_lock_irqsave(&qhp->lock, flag);
+ qhp->attr.state = state;
+ spin_unlock_irqrestore(&qhp->lock, flag);
+}
+
+static void dealloc_oc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
+{
+ c4iw_ocqp_pool_free(rdev, sq->dma_addr, sq->memsize);
+}
+
+static void dealloc_host_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
+{
+ dma_free_coherent(&(rdev->lldi.pdev->dev), sq->memsize, sq->queue,
+ pci_unmap_addr(sq, mapping));
+}
+
+static void dealloc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
+{
+ if (t4_sq_onchip(sq))
+ dealloc_oc_sq(rdev, sq);
+ else
+ dealloc_host_sq(rdev, sq);
+}
+
+static int alloc_oc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
+{
+ if (!ocqp_support || !t4_ocqp_supported())
+ return -ENOSYS;
+ sq->dma_addr = c4iw_ocqp_pool_alloc(rdev, sq->memsize);
+ if (!sq->dma_addr)
+ return -ENOMEM;
+ sq->phys_addr = rdev->oc_mw_pa + sq->dma_addr -
+ rdev->lldi.vr->ocq.start;
+ sq->queue = (__force union t4_wr *)(rdev->oc_mw_kva + sq->dma_addr -
+ rdev->lldi.vr->ocq.start);
+ sq->flags |= T4_SQ_ONCHIP;
+ return 0;
+}
+
+static int alloc_host_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
+{
+ sq->queue = dma_alloc_coherent(&(rdev->lldi.pdev->dev), sq->memsize,
+ &(sq->dma_addr), GFP_KERNEL);
+ if (!sq->queue)
+ return -ENOMEM;
+ sq->phys_addr = virt_to_phys(sq->queue);
+ pci_unmap_addr_set(sq, mapping, sq->dma_addr);
+ return 0;
+}
+
static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
struct c4iw_dev_ucontext *uctx)
{
@@ -41,9 +98,7 @@ static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
dma_free_coherent(&(rdev->lldi.pdev->dev),
wq->rq.memsize, wq->rq.queue,
dma_unmap_addr(&wq->rq, mapping));
- dma_free_coherent(&(rdev->lldi.pdev->dev),
- wq->sq.memsize, wq->sq.queue,
- dma_unmap_addr(&wq->sq, mapping));
+ dealloc_sq(rdev, &wq->sq);
c4iw_rqtpool_free(rdev, wq->rq.rqt_hwaddr, wq->rq.rqt_size);
kfree(wq->rq.sw_rq);
kfree(wq->sq.sw_sq);
@@ -93,11 +148,12 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
if (!wq->rq.rqt_hwaddr)
goto err4;
- wq->sq.queue = dma_alloc_coherent(&(rdev->lldi.pdev->dev),
- wq->sq.memsize, &(wq->sq.dma_addr),
- GFP_KERNEL);
- if (!wq->sq.queue)
- goto err5;
+ if (user) {
+ if (alloc_oc_sq(rdev, &wq->sq) && alloc_host_sq(rdev, &wq->sq))
+ goto err5;
+ } else
+ if (alloc_host_sq(rdev, &wq->sq))
+ goto err5;
memset(wq->sq.queue, 0, wq->sq.memsize);
dma_unmap_addr_set(&wq->sq, mapping, wq->sq.dma_addr);
@@ -144,7 +200,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
V_FW_RI_RES_WR_NRES(2) |
FW_WR_COMPL(1));
res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16));
- res_wr->cookie = (u64)&wr_wait;
+ res_wr->cookie = (unsigned long) &wr_wait;
res = res_wr->res;
res->u.sqrq.restype = FW_RI_RES_TYPE_SQ;
res->u.sqrq.op = FW_RI_RES_OP_WRITE;
@@ -158,6 +214,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
V_FW_RI_RES_WR_HOSTFCMODE(0) | /* no host cidx updates */
V_FW_RI_RES_WR_CPRIO(0) | /* don't keep in chip cache */
V_FW_RI_RES_WR_PCIECHN(0) | /* set by uP at ri_init time */
+ t4_sq_onchip(&wq->sq) ? F_FW_RI_RES_WR_ONCHIP : 0 |
V_FW_RI_RES_WR_IQID(scq->cqid));
res->u.sqrq.dcaen_to_eqsize = cpu_to_be32(
V_FW_RI_RES_WR_DCAEN(0) |
@@ -198,14 +255,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
ret = c4iw_ofld_send(rdev, skb);
if (ret)
goto err7;
- wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO);
- if (!wr_wait.done) {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(rdev->lldi.pdev));
- rdev->flags = T4_FATAL_ERROR;
- ret = -EIO;
- } else
- ret = wr_wait.ret;
+ ret = c4iw_wait_for_reply(rdev, &wr_wait, 0, wq->sq.qid, __func__);
if (ret)
goto err7;
@@ -219,9 +269,7 @@ err7:
wq->rq.memsize, wq->rq.queue,
dma_unmap_addr(&wq->rq, mapping));
err6:
- dma_free_coherent(&(rdev->lldi.pdev->dev),
- wq->sq.memsize, wq->sq.queue,
- dma_unmap_addr(&wq->sq, mapping));
+ dealloc_sq(rdev, &wq->sq);
err5:
c4iw_rqtpool_free(rdev, wq->rq.rqt_hwaddr, wq->rq.rqt_size);
err4:
@@ -263,6 +311,9 @@ static int build_immd(struct t4_sq *sq, struct fw_ri_immd *immdp,
rem -= len;
}
}
+ len = roundup(plen + sizeof *immdp, 16) - (plen + sizeof *immdp);
+ if (len)
+ memset(dstp, 0, len);
immdp->op = FW_RI_DATA_IMMD;
immdp->r1 = 0;
immdp->r2 = 0;
@@ -292,6 +343,7 @@ static int build_isgl(__be64 *queue_start, __be64 *queue_end,
if (++flitp == queue_end)
flitp = queue_start;
}
+ *flitp = (__force __be64)0;
isglp->op = FW_RI_DATA_ISGL;
isglp->r1 = 0;
isglp->nsge = cpu_to_be16(num_sge);
@@ -453,13 +505,15 @@ static int build_rdma_recv(struct c4iw_qp *qhp, union t4_recv_wr *wqe,
return 0;
}
-static int build_fastreg(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16)
+static int build_fastreg(struct t4_sq *sq, union t4_wr *wqe,
+ struct ib_send_wr *wr, u8 *len16)
{
struct fw_ri_immd *imdp;
__be64 *p;
int i;
int pbllen = roundup(wr->wr.fast_reg.page_list_len * sizeof(u64), 32);
+ int rem;
if (wr->wr.fast_reg.page_list_len > T4_MAX_FR_DEPTH)
return -EINVAL;
@@ -474,32 +528,28 @@ static int build_fastreg(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16)
wqe->fr.va_hi = cpu_to_be32(wr->wr.fast_reg.iova_start >> 32);
wqe->fr.va_lo_fbo = cpu_to_be32(wr->wr.fast_reg.iova_start &
0xffffffff);
- if (pbllen > T4_MAX_FR_IMMD) {
- struct c4iw_fr_page_list *c4pl =
- to_c4iw_fr_page_list(wr->wr.fast_reg.page_list);
- struct fw_ri_dsgl *sglp;
-
- sglp = (struct fw_ri_dsgl *)(&wqe->fr + 1);
- sglp->op = FW_RI_DATA_DSGL;
- sglp->r1 = 0;
- sglp->nsge = cpu_to_be16(1);
- sglp->addr0 = cpu_to_be64(c4pl->dma_addr);
- sglp->len0 = cpu_to_be32(pbllen);
-
- *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *sglp, 16);
- } else {
- imdp = (struct fw_ri_immd *)(&wqe->fr + 1);
- imdp->op = FW_RI_DATA_IMMD;
- imdp->r1 = 0;
- imdp->r2 = 0;
- imdp->immdlen = cpu_to_be32(pbllen);
- p = (__be64 *)(imdp + 1);
- for (i = 0; i < wr->wr.fast_reg.page_list_len; i++, p++)
- *p = cpu_to_be64(
- (u64)wr->wr.fast_reg.page_list->page_list[i]);
- *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *imdp + pbllen,
- 16);
+ WARN_ON(pbllen > T4_MAX_FR_IMMD);
+ imdp = (struct fw_ri_immd *)(&wqe->fr + 1);
+ imdp->op = FW_RI_DATA_IMMD;
+ imdp->r1 = 0;
+ imdp->r2 = 0;
+ imdp->immdlen = cpu_to_be32(pbllen);
+ p = (__be64 *)(imdp + 1);
+ rem = pbllen;
+ for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {
+ *p = cpu_to_be64((u64)wr->wr.fast_reg.page_list->page_list[i]);
+ rem -= sizeof *p;
+ if (++p == (__be64 *)&sq->queue[sq->size])
+ p = (__be64 *)sq->queue;
}
+ BUG_ON(rem < 0);
+ while (rem) {
+ *p = 0;
+ rem -= sizeof *p;
+ if (++p == (__be64 *)&sq->queue[sq->size])
+ p = (__be64 *)sq->queue;
+ }
+ *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *imdp + pbllen, 16);
return 0;
}
@@ -587,7 +637,7 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
fw_opcode = FW_RI_RDMA_READ_WR;
swsqe->opcode = FW_RI_READ_REQ;
if (wr->opcode == IB_WR_RDMA_READ_WITH_INV)
- fw_flags |= FW_RI_RDMA_READ_INVALIDATE;
+ fw_flags = FW_RI_RDMA_READ_INVALIDATE;
else
fw_flags = 0;
err = build_rdma_read(wqe, wr, &len16);
@@ -600,7 +650,7 @@ int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
case IB_WR_FAST_REG_MR:
fw_opcode = FW_RI_FR_NSMR_WR;
swsqe->opcode = FW_RI_FAST_REGISTER;
- err = build_fastreg(wqe, wr, &len16);
+ err = build_fastreg(&qhp->wq.sq, wqe, wr, &len16);
break;
case IB_WR_LOCAL_INV:
if (wr->send_flags & IB_SEND_FENCE)
@@ -905,46 +955,38 @@ static void post_terminate(struct c4iw_qp *qhp, struct t4_cqe *err_cqe,
* Assumes qhp lock is held.
*/
static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp,
- struct c4iw_cq *schp, unsigned long *flag)
+ struct c4iw_cq *schp)
{
int count;
int flushed;
+ unsigned long flag;
PDBG("%s qhp %p rchp %p schp %p\n", __func__, qhp, rchp, schp);
- /* take a ref on the qhp since we must release the lock */
- atomic_inc(&qhp->refcnt);
- spin_unlock_irqrestore(&qhp->lock, *flag);
/* locking hierarchy: cq lock first, then qp lock. */
- spin_lock_irqsave(&rchp->lock, *flag);
+ spin_lock_irqsave(&rchp->lock, flag);
spin_lock(&qhp->lock);
c4iw_flush_hw_cq(&rchp->cq);
c4iw_count_rcqes(&rchp->cq, &qhp->wq, &count);
flushed = c4iw_flush_rq(&qhp->wq, &rchp->cq, count);
spin_unlock(&qhp->lock);
- spin_unlock_irqrestore(&rchp->lock, *flag);
+ spin_unlock_irqrestore(&rchp->lock, flag);
if (flushed)
(*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
/* locking hierarchy: cq lock first, then qp lock. */
- spin_lock_irqsave(&schp->lock, *flag);
+ spin_lock_irqsave(&schp->lock, flag);
spin_lock(&qhp->lock);
c4iw_flush_hw_cq(&schp->cq);
c4iw_count_scqes(&schp->cq, &qhp->wq, &count);
flushed = c4iw_flush_sq(&qhp->wq, &schp->cq, count);
spin_unlock(&qhp->lock);
- spin_unlock_irqrestore(&schp->lock, *flag);
+ spin_unlock_irqrestore(&schp->lock, flag);
if (flushed)
(*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context);
-
- /* deref */
- if (atomic_dec_and_test(&qhp->refcnt))
- wake_up(&qhp->wait);
-
- spin_lock_irqsave(&qhp->lock, *flag);
}
-static void flush_qp(struct c4iw_qp *qhp, unsigned long *flag)
+static void flush_qp(struct c4iw_qp *qhp)
{
struct c4iw_cq *rchp, *schp;
@@ -958,7 +1000,7 @@ static void flush_qp(struct c4iw_qp *qhp, unsigned long *flag)
t4_set_cq_in_error(&schp->cq);
return;
}
- __flush_qp(qhp, rchp, schp, flag);
+ __flush_qp(qhp, rchp, schp);
}
static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
@@ -966,7 +1008,6 @@ static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
{
struct fw_ri_wr *wqe;
int ret;
- struct c4iw_wr_wait wr_wait;
struct sk_buff *skb;
PDBG("%s qhp %p qid 0x%x tid %u\n", __func__, qhp, qhp->wq.sq.qid,
@@ -985,28 +1026,16 @@ static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
wqe->flowid_len16 = cpu_to_be32(
FW_WR_FLOWID(ep->hwtid) |
FW_WR_LEN16(DIV_ROUND_UP(sizeof *wqe, 16)));
- wqe->cookie = (u64)&wr_wait;
+ wqe->cookie = (unsigned long) &ep->com.wr_wait;
wqe->u.fini.type = FW_RI_TYPE_FINI;
- c4iw_init_wr_wait(&wr_wait);
+ c4iw_init_wr_wait(&ep->com.wr_wait);
ret = c4iw_ofld_send(&rhp->rdev, skb);
if (ret)
goto out;
- wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO);
- if (!wr_wait.done) {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(rhp->rdev.lldi.pdev));
- rhp->rdev.flags = T4_FATAL_ERROR;
- ret = -EIO;
- } else {
- ret = wr_wait.ret;
- if (ret)
- printk(KERN_WARNING MOD
- "%s: Abnormal close qpid %d ret %u\n",
- pci_name(rhp->rdev.lldi.pdev), qhp->wq.sq.qid,
- ret);
- }
+ ret = c4iw_wait_for_reply(&rhp->rdev, &ep->com.wr_wait, qhp->ep->hwtid,
+ qhp->wq.sq.qid, __func__);
out:
PDBG("%s ret %d\n", __func__, ret);
return ret;
@@ -1040,7 +1069,6 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp)
{
struct fw_ri_wr *wqe;
int ret;
- struct c4iw_wr_wait wr_wait;
struct sk_buff *skb;
PDBG("%s qhp %p qid 0x%x tid %u\n", __func__, qhp, qhp->wq.sq.qid,
@@ -1060,7 +1088,7 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp)
FW_WR_FLOWID(qhp->ep->hwtid) |
FW_WR_LEN16(DIV_ROUND_UP(sizeof *wqe, 16)));
- wqe->cookie = (u64)&wr_wait;
+ wqe->cookie = (unsigned long) &qhp->ep->com.wr_wait;
wqe->u.init.type = FW_RI_TYPE_INIT;
wqe->u.init.mpareqbit_p2ptype =
@@ -1097,19 +1125,13 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp)
if (qhp->attr.mpa_attr.initiator)
build_rtr_msg(qhp->attr.mpa_attr.p2p_type, &wqe->u.init);
- c4iw_init_wr_wait(&wr_wait);
+ c4iw_init_wr_wait(&qhp->ep->com.wr_wait);
ret = c4iw_ofld_send(&rhp->rdev, skb);
if (ret)
goto out;
- wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO);
- if (!wr_wait.done) {
- printk(KERN_ERR MOD "Device %s not responding!\n",
- pci_name(rhp->rdev.lldi.pdev));
- rhp->rdev.flags = T4_FATAL_ERROR;
- ret = -EIO;
- } else
- ret = wr_wait.ret;
+ ret = c4iw_wait_for_reply(&rhp->rdev, &qhp->ep->com.wr_wait,
+ qhp->ep->hwtid, qhp->wq.sq.qid, __func__);
out:
PDBG("%s ret %d\n", __func__, ret);
return ret;
@@ -1122,7 +1144,6 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
{
int ret = 0;
struct c4iw_qp_attributes newattr = qhp->attr;
- unsigned long flag;
int disconnect = 0;
int terminate = 0;
int abort = 0;
@@ -1133,7 +1154,7 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
qhp, qhp->wq.sq.qid, qhp->wq.rq.qid, qhp->ep, qhp->attr.state,
(mask & C4IW_QP_ATTR_NEXT_STATE) ? attrs->next_state : -1);
- spin_lock_irqsave(&qhp->lock, flag);
+ mutex_lock(&qhp->mutex);
/* Process attr changes if in IDLE */
if (mask & C4IW_QP_ATTR_VALID_MODIFY) {
@@ -1184,7 +1205,7 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
qhp->attr.mpa_attr = attrs->mpa_attr;
qhp->attr.llp_stream_handle = attrs->llp_stream_handle;
qhp->ep = qhp->attr.llp_stream_handle;
- qhp->attr.state = C4IW_QP_STATE_RTS;
+ set_state(qhp, C4IW_QP_STATE_RTS);
/*
* Ref the endpoint here and deref when we
@@ -1193,15 +1214,13 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
* transition.
*/
c4iw_get_ep(&qhp->ep->com);
- spin_unlock_irqrestore(&qhp->lock, flag);
ret = rdma_init(rhp, qhp);
- spin_lock_irqsave(&qhp->lock, flag);
if (ret)
goto err;
break;
case C4IW_QP_STATE_ERROR:
- qhp->attr.state = C4IW_QP_STATE_ERROR;
- flush_qp(qhp, &flag);
+ set_state(qhp, C4IW_QP_STATE_ERROR);
+ flush_qp(qhp);
break;
default:
ret = -EINVAL;
@@ -1212,38 +1231,38 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
switch (attrs->next_state) {
case C4IW_QP_STATE_CLOSING:
BUG_ON(atomic_read(&qhp->ep->com.kref.refcount) < 2);
- qhp->attr.state = C4IW_QP_STATE_CLOSING;
+ set_state(qhp, C4IW_QP_STATE_CLOSING);
ep = qhp->ep;
if (!internal) {
abort = 0;
disconnect = 1;
- c4iw_get_ep(&ep->com);
+ c4iw_get_ep(&qhp->ep->com);
}
- spin_unlock_irqrestore(&qhp->lock, flag);
ret = rdma_fini(rhp, qhp, ep);
- spin_lock_irqsave(&qhp->lock, flag);
if (ret) {
- c4iw_get_ep(&ep->com);
+ if (internal)
+ c4iw_get_ep(&qhp->ep->com);
disconnect = abort = 1;
goto err;
}
break;
case C4IW_QP_STATE_TERMINATE:
- qhp->attr.state = C4IW_QP_STATE_TERMINATE;
+ set_state(qhp, C4IW_QP_STATE_TERMINATE);
if (qhp->ibqp.uobject)
t4_set_wq_in_error(&qhp->wq);
ep = qhp->ep;
- c4iw_get_ep(&ep->com);
- terminate = 1;
+ if (!internal)
+ terminate = 1;
disconnect = 1;
+ c4iw_get_ep(&qhp->ep->com);
break;
case C4IW_QP_STATE_ERROR:
- qhp->attr.state = C4IW_QP_STATE_ERROR;
+ set_state(qhp, C4IW_QP_STATE_ERROR);
if (!internal) {
abort = 1;
disconnect = 1;
ep = qhp->ep;
- c4iw_get_ep(&ep->com);
+ c4iw_get_ep(&qhp->ep->com);
}
goto err;
break;
@@ -1259,8 +1278,8 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
}
switch (attrs->next_state) {
case C4IW_QP_STATE_IDLE:
- flush_qp(qhp, &flag);
- qhp->attr.state = C4IW_QP_STATE_IDLE;
+ flush_qp(qhp);
+ set_state(qhp, C4IW_QP_STATE_IDLE);
qhp->attr.llp_stream_handle = NULL;
c4iw_put_ep(&qhp->ep->com);
qhp->ep = NULL;
@@ -1282,7 +1301,7 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
ret = -EINVAL;
goto out;
}
- qhp->attr.state = C4IW_QP_STATE_IDLE;
+ set_state(qhp, C4IW_QP_STATE_IDLE);
break;
case C4IW_QP_STATE_TERMINATE:
if (!internal) {
@@ -1305,15 +1324,16 @@ err:
/* disassociate the LLP connection */
qhp->attr.llp_stream_handle = NULL;
- ep = qhp->ep;
+ if (!ep)
+ ep = qhp->ep;
qhp->ep = NULL;
- qhp->attr.state = C4IW_QP_STATE_ERROR;
+ set_state(qhp, C4IW_QP_STATE_ERROR);
free = 1;
wake_up(&qhp->wait);
BUG_ON(!ep);
- flush_qp(qhp, &flag);
+ flush_qp(qhp);
out:
- spin_unlock_irqrestore(&qhp->lock, flag);
+ mutex_unlock(&qhp->mutex);
if (terminate)
post_terminate(qhp, NULL, internal ? GFP_ATOMIC : GFP_KERNEL);
@@ -1335,7 +1355,6 @@ out:
*/
if (free)
c4iw_put_ep(&ep->com);
-
PDBG("%s exit state %d\n", __func__, qhp->attr.state);
return ret;
}
@@ -1380,7 +1399,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
int sqsize, rqsize;
struct c4iw_ucontext *ucontext;
int ret;
- struct c4iw_mm_entry *mm1, *mm2, *mm3, *mm4;
+ struct c4iw_mm_entry *mm1, *mm2, *mm3, *mm4, *mm5 = NULL;
PDBG("%s ib_pd %p\n", __func__, pd);
@@ -1450,6 +1469,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
qhp->attr.max_ord = 1;
qhp->attr.max_ird = 1;
spin_lock_init(&qhp->lock);
+ mutex_init(&qhp->mutex);
init_waitqueue_head(&qhp->wait);
atomic_set(&qhp->refcnt, 1);
@@ -1478,7 +1498,15 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
ret = -ENOMEM;
goto err6;
}
-
+ if (t4_sq_onchip(&qhp->wq.sq)) {
+ mm5 = kmalloc(sizeof *mm5, GFP_KERNEL);
+ if (!mm5) {
+ ret = -ENOMEM;
+ goto err7;
+ }
+ uresp.flags = C4IW_QPF_ONCHIP;
+ } else
+ uresp.flags = 0;
uresp.qid_mask = rhp->rdev.qpmask;
uresp.sqid = qhp->wq.sq.qid;
uresp.sq_size = qhp->wq.sq.size;
@@ -1487,6 +1515,10 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
uresp.rq_size = qhp->wq.rq.size;
uresp.rq_memsize = qhp->wq.rq.memsize;
spin_lock(&ucontext->mmap_lock);
+ if (mm5) {
+ uresp.ma_sync_key = ucontext->key;
+ ucontext->key += PAGE_SIZE;
+ }
uresp.sq_key = ucontext->key;
ucontext->key += PAGE_SIZE;
uresp.rq_key = ucontext->key;
@@ -1498,9 +1530,9 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
spin_unlock(&ucontext->mmap_lock);
ret = ib_copy_to_udata(udata, &uresp, sizeof uresp);
if (ret)
- goto err7;
+ goto err8;
mm1->key = uresp.sq_key;
- mm1->addr = virt_to_phys(qhp->wq.sq.queue);
+ mm1->addr = qhp->wq.sq.phys_addr;
mm1->len = PAGE_ALIGN(qhp->wq.sq.memsize);
insert_mmap(ucontext, mm1);
mm2->key = uresp.rq_key;
@@ -1515,6 +1547,13 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
mm4->addr = qhp->wq.rq.udb;
mm4->len = PAGE_SIZE;
insert_mmap(ucontext, mm4);
+ if (mm5) {
+ mm5->key = uresp.ma_sync_key;
+ mm5->addr = (pci_resource_start(rhp->rdev.lldi.pdev, 0)
+ + A_PCIE_MA_SYNC) & PAGE_MASK;
+ mm5->len = PAGE_SIZE;
+ insert_mmap(ucontext, mm5);
+ }
}
qhp->ibqp.qp_num = qhp->wq.sq.qid;
init_timer(&(qhp->timer));
@@ -1522,6 +1561,8 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
__func__, qhp, qhp->attr.sq_num_entries, qhp->attr.rq_num_entries,
qhp->wq.sq.qid);
return &qhp->ibqp;
+err8:
+ kfree(mm5);
err7:
kfree(mm4);
err6:
diff --git a/drivers/infiniband/hw/cxgb4/resource.c b/drivers/infiniband/hw/cxgb4/resource.c
index 83b23dfa250d..4fb50d58b493 100644
--- a/drivers/infiniband/hw/cxgb4/resource.c
+++ b/drivers/infiniband/hw/cxgb4/resource.c
@@ -311,6 +311,9 @@ u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
{
unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);
PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size);
+ if (!addr && printk_ratelimit())
+ printk(KERN_WARNING MOD "%s: Out of PBL memory\n",
+ pci_name(rdev->lldi.pdev));
return (u32)addr;
}
@@ -370,6 +373,9 @@ u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
{
unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);
PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size << 6);
+ if (!addr && printk_ratelimit())
+ printk(KERN_WARNING MOD "%s: Out of RQT memory\n",
+ pci_name(rdev->lldi.pdev));
return (u32)addr;
}
@@ -416,3 +422,59 @@ void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
{
gen_pool_destroy(rdev->rqt_pool);
}
+
+/*
+ * On-Chip QP Memory.
+ */
+#define MIN_OCQP_SHIFT 12 /* 4KB == min ocqp size */
+
+u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size)
+{
+ unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size);
+ PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size);
+ return (u32)addr;
+}
+
+void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size)
+{
+ PDBG("%s addr 0x%x size %d\n", __func__, addr, size);
+ gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size);
+}
+
+int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev)
+{
+ unsigned start, chunk, top;
+
+ rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1);
+ if (!rdev->ocqp_pool)
+ return -ENOMEM;
+
+ start = rdev->lldi.vr->ocq.start;
+ chunk = rdev->lldi.vr->ocq.size;
+ top = start + chunk;
+
+ while (start < top) {
+ chunk = min(top - start + 1, chunk);
+ if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) {
+ PDBG("%s failed to add OCQP chunk (%x/%x)\n",
+ __func__, start, chunk);
+ if (chunk <= 1024 << MIN_OCQP_SHIFT) {
+ printk(KERN_WARNING MOD
+ "Failed to add all OCQP chunks (%x/%x)\n",
+ start, top - start);
+ return 0;
+ }
+ chunk >>= 1;
+ } else {
+ PDBG("%s added OCQP chunk (%x/%x)\n",
+ __func__, start, chunk);
+ start += chunk;
+ }
+ }
+ return 0;
+}
+
+void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)
+{
+ gen_pool_destroy(rdev->ocqp_pool);
+}
diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h
index 24f369046ef3..70004425d695 100644
--- a/drivers/infiniband/hw/cxgb4/t4.h
+++ b/drivers/infiniband/hw/cxgb4/t4.h
@@ -52,6 +52,7 @@
#define T4_STAG_UNSET 0xffffffff
#define T4_FW_MAJ 0
#define T4_EQ_STATUS_ENTRIES (L1_CACHE_BYTES > 64 ? 2 : 1)
+#define A_PCIE_MA_SYNC 0x30b4
struct t4_status_page {
__be32 rsvd1; /* flit 0 - hw owns */
@@ -65,7 +66,7 @@ struct t4_status_page {
#define T4_EQ_ENTRY_SIZE 64
-#define T4_SQ_NUM_SLOTS 4
+#define T4_SQ_NUM_SLOTS 5
#define T4_SQ_NUM_BYTES (T4_EQ_ENTRY_SIZE * T4_SQ_NUM_SLOTS)
#define T4_MAX_SEND_SGE ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_send_wr) - \
sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge))
@@ -78,7 +79,7 @@ struct t4_status_page {
sizeof(struct fw_ri_rdma_write_wr) - \
sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge))
#define T4_MAX_FR_IMMD ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_fr_nsmr_wr) - \
- sizeof(struct fw_ri_immd)))
+ sizeof(struct fw_ri_immd)) & ~31UL)
#define T4_MAX_FR_DEPTH (T4_MAX_FR_IMMD / sizeof(u64))
#define T4_RQ_NUM_SLOTS 2
@@ -266,10 +267,36 @@ struct t4_swsqe {
u16 idx;
};
+static inline pgprot_t t4_pgprot_wc(pgprot_t prot)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ return pgprot_writecombine(prot);
+#elif defined(CONFIG_PPC64)
+ return __pgprot((pgprot_val(prot) | _PAGE_NO_CACHE) &
+ ~(pgprot_t)_PAGE_GUARDED);
+#else
+ return pgprot_noncached(prot);
+#endif
+}
+
+static inline int t4_ocqp_supported(void)
+{
+#if defined(__i386__) || defined(__x86_64__) || defined(CONFIG_PPC64)
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+enum {
+ T4_SQ_ONCHIP = (1<<0),
+};
+
struct t4_sq {
union t4_wr *queue;
dma_addr_t dma_addr;
DEFINE_DMA_UNMAP_ADDR(mapping);
+ unsigned long phys_addr;
struct t4_swsqe *sw_sq;
struct t4_swsqe *oldest_read;
u64 udb;
@@ -280,6 +307,7 @@ struct t4_sq {
u16 cidx;
u16 pidx;
u16 wq_pidx;
+ u16 flags;
};
struct t4_swrqe {
@@ -350,6 +378,11 @@ static inline void t4_rq_consume(struct t4_wq *wq)
wq->rq.cidx = 0;
}
+static inline int t4_sq_onchip(struct t4_sq *sq)
+{
+ return sq->flags & T4_SQ_ONCHIP;
+}
+
static inline int t4_sq_empty(struct t4_wq *wq)
{
return wq->sq.in_use == 0;
@@ -396,30 +429,27 @@ static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc)
static inline int t4_wq_in_error(struct t4_wq *wq)
{
- return wq->sq.queue[wq->sq.size].status.qp_err;
+ return wq->rq.queue[wq->rq.size].status.qp_err;
}
static inline void t4_set_wq_in_error(struct t4_wq *wq)
{
- wq->sq.queue[wq->sq.size].status.qp_err = 1;
wq->rq.queue[wq->rq.size].status.qp_err = 1;
}
static inline void t4_disable_wq_db(struct t4_wq *wq)
{
- wq->sq.queue[wq->sq.size].status.db_off = 1;
wq->rq.queue[wq->rq.size].status.db_off = 1;
}
static inline void t4_enable_wq_db(struct t4_wq *wq)
{
- wq->sq.queue[wq->sq.size].status.db_off = 0;
wq->rq.queue[wq->rq.size].status.db_off = 0;
}
static inline int t4_wq_db_enabled(struct t4_wq *wq)
{
- return !wq->sq.queue[wq->sq.size].status.db_off;
+ return !wq->rq.queue[wq->rq.size].status.db_off;
}
struct t4_cq {
diff --git a/drivers/infiniband/hw/cxgb4/user.h b/drivers/infiniband/hw/cxgb4/user.h
index ed6414abde02..e6669d54770e 100644
--- a/drivers/infiniband/hw/cxgb4/user.h
+++ b/drivers/infiniband/hw/cxgb4/user.h
@@ -50,7 +50,13 @@ struct c4iw_create_cq_resp {
__u32 qid_mask;
};
+
+enum {
+ C4IW_QPF_ONCHIP = (1<<0)
+};
+
struct c4iw_create_qp_resp {
+ __u64 ma_sync_key;
__u64 sq_key;
__u64 rq_key;
__u64 sq_db_gts_key;
@@ -62,5 +68,6 @@ struct c4iw_create_qp_resp {
__u32 sq_size;
__u32 rq_size;
__u32 qid_mask;
+ __u32 flags;
};
#endif
diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c
index 53f4cd4fc19a..43cae84005f0 100644
--- a/drivers/infiniband/hw/ehca/ehca_mrmw.c
+++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c
@@ -171,7 +171,7 @@ struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags)
}
ret = ehca_reg_maxmr(shca, e_maxmr,
- (void *)ehca_map_vaddr((void *)KERNELBASE),
+ (void *)ehca_map_vaddr((void *)(KERNELBASE + PHYSICAL_START)),
mr_access_flags, e_pd,
&e_maxmr->ib.ib_mr.lkey,
&e_maxmr->ib.ib_mr.rkey);
@@ -1636,7 +1636,7 @@ int ehca_reg_internal_maxmr(
/* register internal max-MR on HCA */
size_maxmr = ehca_mr_len;
- iova_start = (u64 *)ehca_map_vaddr((void *)KERNELBASE);
+ iova_start = (u64 *)ehca_map_vaddr((void *)(KERNELBASE + PHYSICAL_START));
ib_pbuf.addr = 0;
ib_pbuf.size = size_maxmr;
num_kpages = NUM_CHUNKS(((u64)iova_start % PAGE_SIZE) + size_maxmr,
@@ -2209,7 +2209,7 @@ int ehca_mr_is_maxmr(u64 size,
{
/* a MR is treated as max-MR only if it fits following: */
if ((size == ehca_mr_len) &&
- (iova_start == (void *)ehca_map_vaddr((void *)KERNELBASE))) {
+ (iova_start == (void *)ehca_map_vaddr((void *)(KERNELBASE + PHYSICAL_START)))) {
ehca_gen_dbg("this is a max-MR");
return 1;
} else
diff --git a/drivers/infiniband/hw/ipath/Makefile b/drivers/infiniband/hw/ipath/Makefile
index fa3df82681df..4496f2820c92 100644
--- a/drivers/infiniband/hw/ipath/Makefile
+++ b/drivers/infiniband/hw/ipath/Makefile
@@ -1,4 +1,4 @@
-EXTRA_CFLAGS += -DIPATH_IDSTR='"QLogic kernel.org driver"' \
+ccflags-y := -DIPATH_IDSTR='"QLogic kernel.org driver"' \
-DIPATH_KERN_TYPE=0
obj-$(CONFIG_INFINIBAND_IPATH) += ib_ipath.o
diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c
index d13e72685dcf..12d5bf76302c 100644
--- a/drivers/infiniband/hw/ipath/ipath_fs.c
+++ b/drivers/infiniband/hw/ipath/ipath_fs.c
@@ -57,6 +57,7 @@ static int ipathfs_mknod(struct inode *dir, struct dentry *dentry,
goto bail;
}
+ inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_private = data;
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index 11a236f8d884..4b8f9c49397e 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -30,66 +30,163 @@
* SOFTWARE.
*/
+#include <rdma/ib_addr.h>
+#include <rdma/ib_cache.h>
+
#include <linux/slab.h>
+#include <linux/inet.h>
+#include <linux/string.h>
#include "mlx4_ib.h"
-struct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+int mlx4_ib_resolve_grh(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah_attr,
+ u8 *mac, int *is_mcast, u8 port)
{
- struct mlx4_dev *dev = to_mdev(pd->device)->dev;
- struct mlx4_ib_ah *ah;
+ struct in6_addr in6;
- ah = kmalloc(sizeof *ah, GFP_ATOMIC);
- if (!ah)
- return ERR_PTR(-ENOMEM);
+ *is_mcast = 0;
- memset(&ah->av, 0, sizeof ah->av);
+ memcpy(&in6, ah_attr->grh.dgid.raw, sizeof in6);
+ if (rdma_link_local_addr(&in6))
+ rdma_get_ll_mac(&in6, mac);
+ else if (rdma_is_multicast_addr(&in6)) {
+ rdma_get_mcast_mac(&in6, mac);
+ *is_mcast = 1;
+ } else
+ return -EINVAL;
- ah->av.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
- ah->av.g_slid = ah_attr->src_path_bits;
- ah->av.dlid = cpu_to_be16(ah_attr->dlid);
- if (ah_attr->static_rate) {
- ah->av.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
- while (ah->av.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET &&
- !(1 << ah->av.stat_rate & dev->caps.stat_rate_support))
- --ah->av.stat_rate;
- }
- ah->av.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
+ return 0;
+}
+
+static struct ib_ah *create_ib_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
+ struct mlx4_ib_ah *ah)
+{
+ struct mlx4_dev *dev = to_mdev(pd->device)->dev;
+
+ ah->av.ib.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
+ ah->av.ib.g_slid = ah_attr->src_path_bits;
if (ah_attr->ah_flags & IB_AH_GRH) {
- ah->av.g_slid |= 0x80;
- ah->av.gid_index = ah_attr->grh.sgid_index;
- ah->av.hop_limit = ah_attr->grh.hop_limit;
- ah->av.sl_tclass_flowlabel |=
+ ah->av.ib.g_slid |= 0x80;
+ ah->av.ib.gid_index = ah_attr->grh.sgid_index;
+ ah->av.ib.hop_limit = ah_attr->grh.hop_limit;
+ ah->av.ib.sl_tclass_flowlabel |=
cpu_to_be32((ah_attr->grh.traffic_class << 20) |
ah_attr->grh.flow_label);
- memcpy(ah->av.dgid, ah_attr->grh.dgid.raw, 16);
+ memcpy(ah->av.ib.dgid, ah_attr->grh.dgid.raw, 16);
+ }
+
+ ah->av.ib.dlid = cpu_to_be16(ah_attr->dlid);
+ if (ah_attr->static_rate) {
+ ah->av.ib.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
+ while (ah->av.ib.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET &&
+ !(1 << ah->av.ib.stat_rate & dev->caps.stat_rate_support))
+ --ah->av.ib.stat_rate;
}
+ ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
return &ah->ibah;
}
+static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
+ struct mlx4_ib_ah *ah)
+{
+ struct mlx4_ib_dev *ibdev = to_mdev(pd->device);
+ struct mlx4_dev *dev = ibdev->dev;
+ union ib_gid sgid;
+ u8 mac[6];
+ int err;
+ int is_mcast;
+ u16 vlan_tag;
+
+ err = mlx4_ib_resolve_grh(ibdev, ah_attr, mac, &is_mcast, ah_attr->port_num);
+ if (err)
+ return ERR_PTR(err);
+
+ memcpy(ah->av.eth.mac, mac, 6);
+ err = ib_get_cached_gid(pd->device, ah_attr->port_num, ah_attr->grh.sgid_index, &sgid);
+ if (err)
+ return ERR_PTR(err);
+ vlan_tag = rdma_get_vlan_id(&sgid);
+ if (vlan_tag < 0x1000)
+ vlan_tag |= (ah_attr->sl & 7) << 13;
+ ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
+ ah->av.eth.gid_index = ah_attr->grh.sgid_index;
+ ah->av.eth.vlan = cpu_to_be16(vlan_tag);
+ if (ah_attr->static_rate) {
+ ah->av.eth.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
+ while (ah->av.eth.stat_rate > IB_RATE_2_5_GBPS + MLX4_STAT_RATE_OFFSET &&
+ !(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support))
+ --ah->av.eth.stat_rate;
+ }
+
+ /*
+ * HW requires multicast LID so we just choose one.
+ */
+ if (is_mcast)
+ ah->av.ib.dlid = cpu_to_be16(0xc000);
+
+ memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16);
+ ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
+
+ return &ah->ibah;
+}
+
+struct ib_ah *mlx4_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+{
+ struct mlx4_ib_ah *ah;
+ struct ib_ah *ret;
+
+ ah = kzalloc(sizeof *ah, GFP_ATOMIC);
+ if (!ah)
+ return ERR_PTR(-ENOMEM);
+
+ if (rdma_port_get_link_layer(pd->device, ah_attr->port_num) == IB_LINK_LAYER_ETHERNET) {
+ if (!(ah_attr->ah_flags & IB_AH_GRH)) {
+ ret = ERR_PTR(-EINVAL);
+ } else {
+ /*
+ * TBD: need to handle the case when we get
+ * called in an atomic context and there we
+ * might sleep. We don't expect this
+ * currently since we're working with link
+ * local addresses which we can translate
+ * without going to sleep.
+ */
+ ret = create_iboe_ah(pd, ah_attr, ah);
+ }
+
+ if (IS_ERR(ret))
+ kfree(ah);
+
+ return ret;
+ } else
+ return create_ib_ah(pd, ah_attr, ah); /* never fails */
+}
+
int mlx4_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr)
{
struct mlx4_ib_ah *ah = to_mah(ibah);
+ enum rdma_link_layer ll;
memset(ah_attr, 0, sizeof *ah_attr);
- ah_attr->dlid = be16_to_cpu(ah->av.dlid);
- ah_attr->sl = be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 28;
- ah_attr->port_num = be32_to_cpu(ah->av.port_pd) >> 24;
- if (ah->av.stat_rate)
- ah_attr->static_rate = ah->av.stat_rate - MLX4_STAT_RATE_OFFSET;
- ah_attr->src_path_bits = ah->av.g_slid & 0x7F;
+ ah_attr->sl = be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28;
+ ah_attr->port_num = be32_to_cpu(ah->av.ib.port_pd) >> 24;
+ ll = rdma_port_get_link_layer(ibah->device, ah_attr->port_num);
+ ah_attr->dlid = ll == IB_LINK_LAYER_INFINIBAND ? be16_to_cpu(ah->av.ib.dlid) : 0;
+ if (ah->av.ib.stat_rate)
+ ah_attr->static_rate = ah->av.ib.stat_rate - MLX4_STAT_RATE_OFFSET;
+ ah_attr->src_path_bits = ah->av.ib.g_slid & 0x7F;
if (mlx4_ib_ah_grh_present(ah)) {
ah_attr->ah_flags = IB_AH_GRH;
ah_attr->grh.traffic_class =
- be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 20;
+ be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 20;
ah_attr->grh.flow_label =
- be32_to_cpu(ah->av.sl_tclass_flowlabel) & 0xfffff;
- ah_attr->grh.hop_limit = ah->av.hop_limit;
- ah_attr->grh.sgid_index = ah->av.gid_index;
- memcpy(ah_attr->grh.dgid.raw, ah->av.dgid, 16);
+ be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) & 0xfffff;
+ ah_attr->grh.hop_limit = ah->av.ib.hop_limit;
+ ah_attr->grh.sgid_index = ah->av.ib.gid_index;
+ memcpy(ah_attr->grh.dgid.raw, ah->av.ib.dgid, 16);
}
return 0;
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index f38d5b118927..c9a8dd63b9e2 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -311,19 +311,25 @@ int mlx4_ib_mad_init(struct mlx4_ib_dev *dev)
struct ib_mad_agent *agent;
int p, q;
int ret;
+ enum rdma_link_layer ll;
- for (p = 0; p < dev->num_ports; ++p)
+ for (p = 0; p < dev->num_ports; ++p) {
+ ll = rdma_port_get_link_layer(&dev->ib_dev, p + 1);
for (q = 0; q <= 1; ++q) {
- agent = ib_register_mad_agent(&dev->ib_dev, p + 1,
- q ? IB_QPT_GSI : IB_QPT_SMI,
- NULL, 0, send_handler,
- NULL, NULL);
- if (IS_ERR(agent)) {
- ret = PTR_ERR(agent);
- goto err;
- }
- dev->send_agent[p][q] = agent;
+ if (ll == IB_LINK_LAYER_INFINIBAND) {
+ agent = ib_register_mad_agent(&dev->ib_dev, p + 1,
+ q ? IB_QPT_GSI : IB_QPT_SMI,
+ NULL, 0, send_handler,
+ NULL, NULL);
+ if (IS_ERR(agent)) {
+ ret = PTR_ERR(agent);
+ goto err;
+ }
+ dev->send_agent[p][q] = agent;
+ } else
+ dev->send_agent[p][q] = NULL;
}
+ }
return 0;
@@ -344,8 +350,10 @@ void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev)
for (p = 0; p < dev->num_ports; ++p) {
for (q = 0; q <= 1; ++q) {
agent = dev->send_agent[p][q];
- dev->send_agent[p][q] = NULL;
- ib_unregister_mad_agent(agent);
+ if (agent) {
+ dev->send_agent[p][q] = NULL;
+ ib_unregister_mad_agent(agent);
+ }
}
if (dev->sm_ah[p])
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 4e94e360e43b..bf3e20cd0298 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -35,9 +35,14 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_vlan.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_user_verbs.h>
+#include <rdma/ib_addr.h>
#include <linux/mlx4/driver.h>
#include <linux/mlx4/cmd.h>
@@ -58,6 +63,15 @@ static const char mlx4_ib_version[] =
DRV_NAME ": Mellanox ConnectX InfiniBand driver v"
DRV_VERSION " (" DRV_RELDATE ")\n";
+struct update_gid_work {
+ struct work_struct work;
+ union ib_gid gids[128];
+ struct mlx4_ib_dev *dev;
+ int port;
+};
+
+static struct workqueue_struct *wq;
+
static void init_query_mad(struct ib_smp *mad)
{
mad->base_version = 1;
@@ -66,6 +80,8 @@ static void init_query_mad(struct ib_smp *mad)
mad->method = IB_MGMT_METHOD_GET;
}
+static union ib_gid zgid;
+
static int mlx4_ib_query_device(struct ib_device *ibdev,
struct ib_device_attr *props)
{
@@ -135,7 +151,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs;
props->max_srq_wr = dev->dev->caps.max_srq_wqes - 1;
props->max_srq_sge = dev->dev->caps.max_srq_sge;
- props->max_fast_reg_page_list_len = PAGE_SIZE / sizeof (u64);
+ props->max_fast_reg_page_list_len = MLX4_MAX_FAST_REG_PAGES;
props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay;
props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ?
IB_ATOMIC_HCA : IB_ATOMIC_NONE;
@@ -154,28 +170,19 @@ out:
return err;
}
-static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port,
- struct ib_port_attr *props)
+static enum rdma_link_layer
+mlx4_ib_port_link_layer(struct ib_device *device, u8 port_num)
{
- struct ib_smp *in_mad = NULL;
- struct ib_smp *out_mad = NULL;
- int err = -ENOMEM;
-
- in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
- out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
- if (!in_mad || !out_mad)
- goto out;
-
- memset(props, 0, sizeof *props);
-
- init_query_mad(in_mad);
- in_mad->attr_id = IB_SMP_ATTR_PORT_INFO;
- in_mad->attr_mod = cpu_to_be32(port);
+ struct mlx4_dev *dev = to_mdev(device)->dev;
- err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad);
- if (err)
- goto out;
+ return dev->caps.port_mask & (1 << (port_num - 1)) ?
+ IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET;
+}
+static int ib_link_query_port(struct ib_device *ibdev, u8 port,
+ struct ib_port_attr *props,
+ struct ib_smp *out_mad)
+{
props->lid = be16_to_cpup((__be16 *) (out_mad->data + 16));
props->lmc = out_mad->data[34] & 0x7;
props->sm_lid = be16_to_cpup((__be16 *) (out_mad->data + 18));
@@ -196,6 +203,80 @@ static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port,
props->max_vl_num = out_mad->data[37] >> 4;
props->init_type_reply = out_mad->data[41] >> 4;
+ return 0;
+}
+
+static u8 state_to_phys_state(enum ib_port_state state)
+{
+ return state == IB_PORT_ACTIVE ? 5 : 3;
+}
+
+static int eth_link_query_port(struct ib_device *ibdev, u8 port,
+ struct ib_port_attr *props,
+ struct ib_smp *out_mad)
+{
+ struct mlx4_ib_iboe *iboe = &to_mdev(ibdev)->iboe;
+ struct net_device *ndev;
+ enum ib_mtu tmp;
+
+ props->active_width = IB_WIDTH_4X;
+ props->active_speed = 4;
+ props->port_cap_flags = IB_PORT_CM_SUP;
+ props->gid_tbl_len = to_mdev(ibdev)->dev->caps.gid_table_len[port];
+ props->max_msg_sz = to_mdev(ibdev)->dev->caps.max_msg_sz;
+ props->pkey_tbl_len = 1;
+ props->bad_pkey_cntr = be16_to_cpup((__be16 *) (out_mad->data + 46));
+ props->qkey_viol_cntr = be16_to_cpup((__be16 *) (out_mad->data + 48));
+ props->max_mtu = IB_MTU_2048;
+ props->subnet_timeout = 0;
+ props->max_vl_num = out_mad->data[37] >> 4;
+ props->init_type_reply = 0;
+ props->state = IB_PORT_DOWN;
+ props->phys_state = state_to_phys_state(props->state);
+ props->active_mtu = IB_MTU_256;
+ spin_lock(&iboe->lock);
+ ndev = iboe->netdevs[port - 1];
+ if (!ndev)
+ goto out;
+
+ tmp = iboe_get_mtu(ndev->mtu);
+ props->active_mtu = tmp ? min(props->max_mtu, tmp) : IB_MTU_256;
+
+ props->state = netif_running(ndev) && netif_oper_up(ndev) ?
+ IB_PORT_ACTIVE : IB_PORT_DOWN;
+ props->phys_state = state_to_phys_state(props->state);
+
+out:
+ spin_unlock(&iboe->lock);
+ return 0;
+}
+
+static int mlx4_ib_query_port(struct ib_device *ibdev, u8 port,
+ struct ib_port_attr *props)
+{
+ struct ib_smp *in_mad = NULL;
+ struct ib_smp *out_mad = NULL;
+ int err = -ENOMEM;
+
+ in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
+ out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
+ if (!in_mad || !out_mad)
+ goto out;
+
+ memset(props, 0, sizeof *props);
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = IB_SMP_ATTR_PORT_INFO;
+ in_mad->attr_mod = cpu_to_be32(port);
+
+ err = mlx4_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ err = mlx4_ib_port_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND ?
+ ib_link_query_port(ibdev, port, props, out_mad) :
+ eth_link_query_port(ibdev, port, props, out_mad);
+
out:
kfree(in_mad);
kfree(out_mad);
@@ -203,8 +284,8 @@ out:
return err;
}
-static int mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
- union ib_gid *gid)
+static int __mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
+ union ib_gid *gid)
{
struct ib_smp *in_mad = NULL;
struct ib_smp *out_mad = NULL;
@@ -241,6 +322,25 @@ out:
return err;
}
+static int iboe_query_gid(struct ib_device *ibdev, u8 port, int index,
+ union ib_gid *gid)
+{
+ struct mlx4_ib_dev *dev = to_mdev(ibdev);
+
+ *gid = dev->iboe.gid_table[port - 1][index];
+
+ return 0;
+}
+
+static int mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
+ union ib_gid *gid)
+{
+ if (rdma_port_get_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND)
+ return __mlx4_ib_query_gid(ibdev, port, index, gid);
+ else
+ return iboe_query_gid(ibdev, port, index, gid);
+}
+
static int mlx4_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
u16 *pkey)
{
@@ -272,14 +372,32 @@ out:
static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask,
struct ib_device_modify *props)
{
+ struct mlx4_cmd_mailbox *mailbox;
+
if (mask & ~IB_DEVICE_MODIFY_NODE_DESC)
return -EOPNOTSUPP;
- if (mask & IB_DEVICE_MODIFY_NODE_DESC) {
- spin_lock(&to_mdev(ibdev)->sm_lock);
- memcpy(ibdev->node_desc, props->node_desc, 64);
- spin_unlock(&to_mdev(ibdev)->sm_lock);
- }
+ if (!(mask & IB_DEVICE_MODIFY_NODE_DESC))
+ return 0;
+
+ spin_lock(&to_mdev(ibdev)->sm_lock);
+ memcpy(ibdev->node_desc, props->node_desc, 64);
+ spin_unlock(&to_mdev(ibdev)->sm_lock);
+
+ /*
+ * If possible, pass node desc to FW, so it can generate
+ * a 144 trap. If cmd fails, just ignore.
+ */
+ mailbox = mlx4_alloc_cmd_mailbox(to_mdev(ibdev)->dev);
+ if (IS_ERR(mailbox))
+ return 0;
+
+ memset(mailbox->buf, 0, 256);
+ memcpy(mailbox->buf, props->node_desc, 64);
+ mlx4_cmd(to_mdev(ibdev)->dev, mailbox->dma, 1, 0,
+ MLX4_CMD_SET_NODE, MLX4_CMD_TIME_CLASS_A);
+
+ mlx4_free_cmd_mailbox(to_mdev(ibdev)->dev, mailbox);
return 0;
}
@@ -289,6 +407,7 @@ static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols,
{
struct mlx4_cmd_mailbox *mailbox;
int err;
+ u8 is_eth = dev->dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH;
mailbox = mlx4_alloc_cmd_mailbox(dev->dev);
if (IS_ERR(mailbox))
@@ -304,7 +423,7 @@ static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols,
((__be32 *) mailbox->buf)[1] = cpu_to_be32(cap_mask);
}
- err = mlx4_cmd(dev->dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT,
+ err = mlx4_cmd(dev->dev, mailbox->dma, port, is_eth, MLX4_CMD_SET_PORT,
MLX4_CMD_TIME_CLASS_B);
mlx4_free_cmd_mailbox(dev->dev, mailbox);
@@ -447,18 +566,132 @@ static int mlx4_ib_dealloc_pd(struct ib_pd *pd)
return 0;
}
+static int add_gid_entry(struct ib_qp *ibqp, union ib_gid *gid)
+{
+ struct mlx4_ib_qp *mqp = to_mqp(ibqp);
+ struct mlx4_ib_dev *mdev = to_mdev(ibqp->device);
+ struct mlx4_ib_gid_entry *ge;
+
+ ge = kzalloc(sizeof *ge, GFP_KERNEL);
+ if (!ge)
+ return -ENOMEM;
+
+ ge->gid = *gid;
+ if (mlx4_ib_add_mc(mdev, mqp, gid)) {
+ ge->port = mqp->port;
+ ge->added = 1;
+ }
+
+ mutex_lock(&mqp->mutex);
+ list_add_tail(&ge->list, &mqp->gid_list);
+ mutex_unlock(&mqp->mutex);
+
+ return 0;
+}
+
+int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp,
+ union ib_gid *gid)
+{
+ u8 mac[6];
+ struct net_device *ndev;
+ int ret = 0;
+
+ if (!mqp->port)
+ return 0;
+
+ spin_lock(&mdev->iboe.lock);
+ ndev = mdev->iboe.netdevs[mqp->port - 1];
+ if (ndev)
+ dev_hold(ndev);
+ spin_unlock(&mdev->iboe.lock);
+
+ if (ndev) {
+ rdma_get_mcast_mac((struct in6_addr *)gid, mac);
+ rtnl_lock();
+ dev_mc_add(mdev->iboe.netdevs[mqp->port - 1], mac);
+ ret = 1;
+ rtnl_unlock();
+ dev_put(ndev);
+ }
+
+ return ret;
+}
+
static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
{
- return mlx4_multicast_attach(to_mdev(ibqp->device)->dev,
- &to_mqp(ibqp)->mqp, gid->raw,
- !!(to_mqp(ibqp)->flags &
- MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK));
+ int err;
+ struct mlx4_ib_dev *mdev = to_mdev(ibqp->device);
+ struct mlx4_ib_qp *mqp = to_mqp(ibqp);
+
+ err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, !!(mqp->flags &
+ MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK));
+ if (err)
+ return err;
+
+ err = add_gid_entry(ibqp, gid);
+ if (err)
+ goto err_add;
+
+ return 0;
+
+err_add:
+ mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw);
+ return err;
+}
+
+static struct mlx4_ib_gid_entry *find_gid_entry(struct mlx4_ib_qp *qp, u8 *raw)
+{
+ struct mlx4_ib_gid_entry *ge;
+ struct mlx4_ib_gid_entry *tmp;
+ struct mlx4_ib_gid_entry *ret = NULL;
+
+ list_for_each_entry_safe(ge, tmp, &qp->gid_list, list) {
+ if (!memcmp(raw, ge->gid.raw, 16)) {
+ ret = ge;
+ break;
+ }
+ }
+
+ return ret;
}
static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
{
- return mlx4_multicast_detach(to_mdev(ibqp->device)->dev,
- &to_mqp(ibqp)->mqp, gid->raw);
+ int err;
+ struct mlx4_ib_dev *mdev = to_mdev(ibqp->device);
+ struct mlx4_ib_qp *mqp = to_mqp(ibqp);
+ u8 mac[6];
+ struct net_device *ndev;
+ struct mlx4_ib_gid_entry *ge;
+
+ err = mlx4_multicast_detach(mdev->dev,
+ &mqp->mqp, gid->raw);
+ if (err)
+ return err;
+
+ mutex_lock(&mqp->mutex);
+ ge = find_gid_entry(mqp, gid->raw);
+ if (ge) {
+ spin_lock(&mdev->iboe.lock);
+ ndev = ge->added ? mdev->iboe.netdevs[ge->port - 1] : NULL;
+ if (ndev)
+ dev_hold(ndev);
+ spin_unlock(&mdev->iboe.lock);
+ rdma_get_mcast_mac((struct in6_addr *)gid, mac);
+ if (ndev) {
+ rtnl_lock();
+ dev_mc_del(mdev->iboe.netdevs[ge->port - 1], mac);
+ rtnl_unlock();
+ dev_put(ndev);
+ }
+ list_del(&ge->list);
+ kfree(ge);
+ } else
+ printk(KERN_WARNING "could not find mgid entry\n");
+
+ mutex_unlock(&mqp->mutex);
+
+ return 0;
}
static int init_node_data(struct mlx4_ib_dev *dev)
@@ -543,15 +776,215 @@ static struct device_attribute *mlx4_class_attributes[] = {
&dev_attr_board_id
};
+static void mlx4_addrconf_ifid_eui48(u8 *eui, u16 vlan_id, struct net_device *dev)
+{
+ memcpy(eui, dev->dev_addr, 3);
+ memcpy(eui + 5, dev->dev_addr + 3, 3);
+ if (vlan_id < 0x1000) {
+ eui[3] = vlan_id >> 8;
+ eui[4] = vlan_id & 0xff;
+ } else {
+ eui[3] = 0xff;
+ eui[4] = 0xfe;
+ }
+ eui[0] ^= 2;
+}
+
+static void update_gids_task(struct work_struct *work)
+{
+ struct update_gid_work *gw = container_of(work, struct update_gid_work, work);
+ struct mlx4_cmd_mailbox *mailbox;
+ union ib_gid *gids;
+ int err;
+ struct mlx4_dev *dev = gw->dev->dev;
+ struct ib_event event;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox)) {
+ printk(KERN_WARNING "update gid table failed %ld\n", PTR_ERR(mailbox));
+ return;
+ }
+
+ gids = mailbox->buf;
+ memcpy(gids, gw->gids, sizeof gw->gids);
+
+ err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port,
+ 1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B);
+ if (err)
+ printk(KERN_WARNING "set port command failed\n");
+ else {
+ memcpy(gw->dev->iboe.gid_table[gw->port - 1], gw->gids, sizeof gw->gids);
+ event.device = &gw->dev->ib_dev;
+ event.element.port_num = gw->port;
+ event.event = IB_EVENT_LID_CHANGE;
+ ib_dispatch_event(&event);
+ }
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ kfree(gw);
+}
+
+static int update_ipv6_gids(struct mlx4_ib_dev *dev, int port, int clear)
+{
+ struct net_device *ndev = dev->iboe.netdevs[port - 1];
+ struct update_gid_work *work;
+ struct net_device *tmp;
+ int i;
+ u8 *hits;
+ int ret;
+ union ib_gid gid;
+ int free;
+ int found;
+ int need_update = 0;
+ u16 vid;
+
+ work = kzalloc(sizeof *work, GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+
+ hits = kzalloc(128, GFP_ATOMIC);
+ if (!hits) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ read_lock(&dev_base_lock);
+ for_each_netdev(&init_net, tmp) {
+ if (ndev && (tmp == ndev || rdma_vlan_dev_real_dev(tmp) == ndev)) {
+ gid.global.subnet_prefix = cpu_to_be64(0xfe80000000000000LL);
+ vid = rdma_vlan_dev_vlan_id(tmp);
+ mlx4_addrconf_ifid_eui48(&gid.raw[8], vid, ndev);
+ found = 0;
+ free = -1;
+ for (i = 0; i < 128; ++i) {
+ if (free < 0 &&
+ !memcmp(&dev->iboe.gid_table[port - 1][i], &zgid, sizeof zgid))
+ free = i;
+ if (!memcmp(&dev->iboe.gid_table[port - 1][i], &gid, sizeof gid)) {
+ hits[i] = 1;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (tmp == ndev &&
+ (memcmp(&dev->iboe.gid_table[port - 1][0],
+ &gid, sizeof gid) ||
+ !memcmp(&dev->iboe.gid_table[port - 1][0],
+ &zgid, sizeof gid))) {
+ dev->iboe.gid_table[port - 1][0] = gid;
+ ++need_update;
+ hits[0] = 1;
+ } else if (free >= 0) {
+ dev->iboe.gid_table[port - 1][free] = gid;
+ hits[free] = 1;
+ ++need_update;
+ }
+ }
+ }
+ }
+ read_unlock(&dev_base_lock);
+
+ for (i = 0; i < 128; ++i)
+ if (!hits[i]) {
+ if (memcmp(&dev->iboe.gid_table[port - 1][i], &zgid, sizeof zgid))
+ ++need_update;
+ dev->iboe.gid_table[port - 1][i] = zgid;
+ }
+
+ if (need_update) {
+ memcpy(work->gids, dev->iboe.gid_table[port - 1], sizeof work->gids);
+ INIT_WORK(&work->work, update_gids_task);
+ work->port = port;
+ work->dev = dev;
+ queue_work(wq, &work->work);
+ } else
+ kfree(work);
+
+ kfree(hits);
+ return 0;
+
+out:
+ kfree(work);
+ return ret;
+}
+
+static void handle_en_event(struct mlx4_ib_dev *dev, int port, unsigned long event)
+{
+ switch (event) {
+ case NETDEV_UP:
+ case NETDEV_CHANGEADDR:
+ update_ipv6_gids(dev, port, 0);
+ break;
+
+ case NETDEV_DOWN:
+ update_ipv6_gids(dev, port, 1);
+ dev->iboe.netdevs[port - 1] = NULL;
+ }
+}
+
+static void netdev_added(struct mlx4_ib_dev *dev, int port)
+{
+ update_ipv6_gids(dev, port, 0);
+}
+
+static void netdev_removed(struct mlx4_ib_dev *dev, int port)
+{
+ update_ipv6_gids(dev, port, 1);
+}
+
+static int mlx4_ib_netdev_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct net_device *dev = ptr;
+ struct mlx4_ib_dev *ibdev;
+ struct net_device *oldnd;
+ struct mlx4_ib_iboe *iboe;
+ int port;
+
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+
+ ibdev = container_of(this, struct mlx4_ib_dev, iboe.nb);
+ iboe = &ibdev->iboe;
+
+ spin_lock(&iboe->lock);
+ mlx4_foreach_ib_transport_port(port, ibdev->dev) {
+ oldnd = iboe->netdevs[port - 1];
+ iboe->netdevs[port - 1] =
+ mlx4_get_protocol_dev(ibdev->dev, MLX4_PROTOCOL_EN, port);
+ if (oldnd != iboe->netdevs[port - 1]) {
+ if (iboe->netdevs[port - 1])
+ netdev_added(ibdev, port);
+ else
+ netdev_removed(ibdev, port);
+ }
+ }
+
+ if (dev == iboe->netdevs[0] ||
+ (iboe->netdevs[0] && rdma_vlan_dev_real_dev(dev) == iboe->netdevs[0]))
+ handle_en_event(ibdev, 1, event);
+ else if (dev == iboe->netdevs[1]
+ || (iboe->netdevs[1] && rdma_vlan_dev_real_dev(dev) == iboe->netdevs[1]))
+ handle_en_event(ibdev, 2, event);
+
+ spin_unlock(&iboe->lock);
+
+ return NOTIFY_DONE;
+}
+
static void *mlx4_ib_add(struct mlx4_dev *dev)
{
struct mlx4_ib_dev *ibdev;
int num_ports = 0;
int i;
+ int err;
+ struct mlx4_ib_iboe *iboe;
printk_once(KERN_INFO "%s", mlx4_ib_version);
- mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB)
+ mlx4_foreach_ib_transport_port(i, dev)
num_ports++;
/* No point in registering a device with no ports... */
@@ -564,6 +997,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
return NULL;
}
+ iboe = &ibdev->iboe;
+
if (mlx4_pd_alloc(dev, &ibdev->priv_pdn))
goto err_dealloc;
@@ -612,6 +1047,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_dev.query_device = mlx4_ib_query_device;
ibdev->ib_dev.query_port = mlx4_ib_query_port;
+ ibdev->ib_dev.get_link_layer = mlx4_ib_port_link_layer;
ibdev->ib_dev.query_gid = mlx4_ib_query_gid;
ibdev->ib_dev.query_pkey = mlx4_ib_query_pkey;
ibdev->ib_dev.modify_device = mlx4_ib_modify_device;
@@ -656,6 +1092,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_dev.unmap_fmr = mlx4_ib_unmap_fmr;
ibdev->ib_dev.dealloc_fmr = mlx4_ib_fmr_dealloc;
+ spin_lock_init(&iboe->lock);
+
if (init_node_data(ibdev))
goto err_map;
@@ -668,16 +1106,28 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
if (mlx4_ib_mad_init(ibdev))
goto err_reg;
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_IBOE && !iboe->nb.notifier_call) {
+ iboe->nb.notifier_call = mlx4_ib_netdev_event;
+ err = register_netdevice_notifier(&iboe->nb);
+ if (err)
+ goto err_reg;
+ }
+
for (i = 0; i < ARRAY_SIZE(mlx4_class_attributes); ++i) {
if (device_create_file(&ibdev->ib_dev.dev,
mlx4_class_attributes[i]))
- goto err_reg;
+ goto err_notif;
}
ibdev->ib_active = true;
return ibdev;
+err_notif:
+ if (unregister_netdevice_notifier(&ibdev->iboe.nb))
+ printk(KERN_WARNING "failure unregistering notifier\n");
+ flush_workqueue(wq);
+
err_reg:
ib_unregister_device(&ibdev->ib_dev);
@@ -703,11 +1153,16 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr)
mlx4_ib_mad_cleanup(ibdev);
ib_unregister_device(&ibdev->ib_dev);
+ if (ibdev->iboe.nb.notifier_call) {
+ if (unregister_netdevice_notifier(&ibdev->iboe.nb))
+ printk(KERN_WARNING "failure unregistering notifier\n");
+ ibdev->iboe.nb.notifier_call = NULL;
+ }
+ iounmap(ibdev->uar_map);
- for (p = 1; p <= ibdev->num_ports; ++p)
+ mlx4_foreach_port(p, dev, MLX4_PORT_TYPE_IB)
mlx4_CLOSE_PORT(dev, p);
- iounmap(ibdev->uar_map);
mlx4_uar_free(dev, &ibdev->priv_uar);
mlx4_pd_free(dev, ibdev->priv_pdn);
ib_dealloc_device(&ibdev->ib_dev);
@@ -747,19 +1202,33 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
}
static struct mlx4_interface mlx4_ib_interface = {
- .add = mlx4_ib_add,
- .remove = mlx4_ib_remove,
- .event = mlx4_ib_event
+ .add = mlx4_ib_add,
+ .remove = mlx4_ib_remove,
+ .event = mlx4_ib_event,
+ .protocol = MLX4_PROTOCOL_IB
};
static int __init mlx4_ib_init(void)
{
- return mlx4_register_interface(&mlx4_ib_interface);
+ int err;
+
+ wq = create_singlethread_workqueue("mlx4_ib");
+ if (!wq)
+ return -ENOMEM;
+
+ err = mlx4_register_interface(&mlx4_ib_interface);
+ if (err) {
+ destroy_workqueue(wq);
+ return err;
+ }
+
+ return 0;
}
static void __exit mlx4_ib_cleanup(void)
{
mlx4_unregister_interface(&mlx4_ib_interface);
+ destroy_workqueue(wq);
}
module_init(mlx4_ib_init);
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index 3486d7675e56..2a322f21049f 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -112,6 +112,13 @@ enum mlx4_ib_qp_flags {
MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK = 1 << 1,
};
+struct mlx4_ib_gid_entry {
+ struct list_head list;
+ union ib_gid gid;
+ int added;
+ u8 port;
+};
+
struct mlx4_ib_qp {
struct ib_qp ibqp;
struct mlx4_qp mqp;
@@ -138,6 +145,8 @@ struct mlx4_ib_qp {
u8 resp_depth;
u8 sq_no_prefetch;
u8 state;
+ int mlx_type;
+ struct list_head gid_list;
};
struct mlx4_ib_srq {
@@ -157,7 +166,14 @@ struct mlx4_ib_srq {
struct mlx4_ib_ah {
struct ib_ah ibah;
- struct mlx4_av av;
+ union mlx4_ext_av av;
+};
+
+struct mlx4_ib_iboe {
+ spinlock_t lock;
+ struct net_device *netdevs[MLX4_MAX_PORTS];
+ struct notifier_block nb;
+ union ib_gid gid_table[MLX4_MAX_PORTS][128];
};
struct mlx4_ib_dev {
@@ -176,6 +192,7 @@ struct mlx4_ib_dev {
struct mutex cap_mask_mutex;
bool ib_active;
+ struct mlx4_ib_iboe iboe;
};
static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev)
@@ -314,9 +331,20 @@ int mlx4_ib_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list, int npages,
int mlx4_ib_unmap_fmr(struct list_head *fmr_list);
int mlx4_ib_fmr_dealloc(struct ib_fmr *fmr);
+int mlx4_ib_resolve_grh(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah_attr,
+ u8 *mac, int *is_mcast, u8 port);
+
static inline int mlx4_ib_ah_grh_present(struct mlx4_ib_ah *ah)
{
- return !!(ah->av.g_slid & 0x80);
+ u8 port = be32_to_cpu(ah->av.ib.port_pd) >> 24 & 3;
+
+ if (rdma_port_get_link_layer(ah->ibah.device, port) == IB_LINK_LAYER_ETHERNET)
+ return 1;
+
+ return !!(ah->av.ib.g_slid & 0x80);
}
+int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp,
+ union ib_gid *gid);
+
#endif /* MLX4_IB_H */
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index 1d27b9a8e2d6..dca55b19a6f1 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -226,7 +226,7 @@ struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device
struct mlx4_ib_fast_reg_page_list *mfrpl;
int size = page_list_len * sizeof (u64);
- if (size > PAGE_SIZE)
+ if (page_list_len > MLX4_MAX_FAST_REG_PAGES)
return ERR_PTR(-EINVAL);
mfrpl = kmalloc(sizeof *mfrpl, GFP_KERNEL);
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 6a60827b2301..9a7794ac34c1 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -33,9 +33,11 @@
#include <linux/log2.h>
#include <linux/slab.h>
+#include <linux/netdevice.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_pack.h>
+#include <rdma/ib_addr.h>
#include <linux/mlx4/qp.h>
@@ -48,17 +50,26 @@ enum {
enum {
MLX4_IB_DEFAULT_SCHED_QUEUE = 0x83,
- MLX4_IB_DEFAULT_QP0_SCHED_QUEUE = 0x3f
+ MLX4_IB_DEFAULT_QP0_SCHED_QUEUE = 0x3f,
+ MLX4_IB_LINK_TYPE_IB = 0,
+ MLX4_IB_LINK_TYPE_ETH = 1
};
enum {
/*
- * Largest possible UD header: send with GRH and immediate data.
+ * Largest possible UD header: send with GRH and immediate
+ * data plus 18 bytes for an Ethernet header with VLAN/802.1Q
+ * tag. (LRH would only use 8 bytes, so Ethernet is the
+ * biggest case)
*/
- MLX4_IB_UD_HEADER_SIZE = 72,
+ MLX4_IB_UD_HEADER_SIZE = 82,
MLX4_IB_LSO_HEADER_SPARE = 128,
};
+enum {
+ MLX4_IB_IBOE_ETHERTYPE = 0x8915
+};
+
struct mlx4_ib_sqp {
struct mlx4_ib_qp qp;
int pkey_index;
@@ -462,6 +473,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
mutex_init(&qp->mutex);
spin_lock_init(&qp->sq.lock);
spin_lock_init(&qp->rq.lock);
+ INIT_LIST_HEAD(&qp->gid_list);
qp->state = IB_QPS_RESET;
if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
@@ -649,6 +661,16 @@ static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *re
}
}
+static void del_gid_entries(struct mlx4_ib_qp *qp)
+{
+ struct mlx4_ib_gid_entry *ge, *tmp;
+
+ list_for_each_entry_safe(ge, tmp, &qp->gid_list, list) {
+ list_del(&ge->list);
+ kfree(ge);
+ }
+}
+
static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
int is_user)
{
@@ -695,6 +717,8 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
if (!qp->ibqp.srq)
mlx4_db_free(dev->dev, &qp->db);
}
+
+ del_gid_entries(qp);
}
struct ib_qp *mlx4_ib_create_qp(struct ib_pd *pd,
@@ -852,6 +876,14 @@ static void mlx4_set_sched(struct mlx4_qp_path *path, u8 port)
static int mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah,
struct mlx4_qp_path *path, u8 port)
{
+ int err;
+ int is_eth = rdma_port_get_link_layer(&dev->ib_dev, port) ==
+ IB_LINK_LAYER_ETHERNET;
+ u8 mac[6];
+ int is_mcast;
+ u16 vlan_tag;
+ int vidx;
+
path->grh_mylmc = ah->src_path_bits & 0x7f;
path->rlid = cpu_to_be16(ah->dlid);
if (ah->static_rate) {
@@ -879,12 +911,49 @@ static int mlx4_set_path(struct mlx4_ib_dev *dev, const struct ib_ah_attr *ah,
memcpy(path->rgid, ah->grh.dgid.raw, 16);
}
- path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE |
- ((port - 1) << 6) | ((ah->sl & 0xf) << 2);
+ if (is_eth) {
+ path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE |
+ ((port - 1) << 6) | ((ah->sl & 7) << 3) | ((ah->sl & 8) >> 1);
+
+ if (!(ah->ah_flags & IB_AH_GRH))
+ return -1;
+
+ err = mlx4_ib_resolve_grh(dev, ah, mac, &is_mcast, port);
+ if (err)
+ return err;
+
+ memcpy(path->dmac, mac, 6);
+ path->ackto = MLX4_IB_LINK_TYPE_ETH;
+ /* use index 0 into MAC table for IBoE */
+ path->grh_mylmc &= 0x80;
+
+ vlan_tag = rdma_get_vlan_id(&dev->iboe.gid_table[port - 1][ah->grh.sgid_index]);
+ if (vlan_tag < 0x1000) {
+ if (mlx4_find_cached_vlan(dev->dev, port, vlan_tag, &vidx))
+ return -ENOENT;
+
+ path->vlan_index = vidx;
+ path->fl = 1 << 6;
+ }
+ } else
+ path->sched_queue = MLX4_IB_DEFAULT_SCHED_QUEUE |
+ ((port - 1) << 6) | ((ah->sl & 0xf) << 2);
return 0;
}
+static void update_mcg_macs(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp)
+{
+ struct mlx4_ib_gid_entry *ge, *tmp;
+
+ list_for_each_entry_safe(ge, tmp, &qp->gid_list, list) {
+ if (!ge->added && mlx4_ib_add_mc(dev, qp, &ge->gid)) {
+ ge->added = 1;
+ ge->port = qp->port;
+ }
+ }
+}
+
static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
const struct ib_qp_attr *attr, int attr_mask,
enum ib_qp_state cur_state, enum ib_qp_state new_state)
@@ -980,7 +1049,7 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
}
if (attr_mask & IB_QP_TIMEOUT) {
- context->pri_path.ackto = attr->timeout << 3;
+ context->pri_path.ackto |= attr->timeout << 3;
optpar |= MLX4_QP_OPTPAR_ACK_TIMEOUT;
}
@@ -1118,8 +1187,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
qp->atomic_rd_en = attr->qp_access_flags;
if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
qp->resp_depth = attr->max_dest_rd_atomic;
- if (attr_mask & IB_QP_PORT)
+ if (attr_mask & IB_QP_PORT) {
qp->port = attr->port_num;
+ update_mcg_macs(dev, qp);
+ }
if (attr_mask & IB_QP_ALT_PATH)
qp->alt_port = attr->alt_port_num;
@@ -1221,40 +1292,59 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
struct mlx4_wqe_mlx_seg *mlx = wqe;
struct mlx4_wqe_inline_seg *inl = wqe + sizeof *mlx;
struct mlx4_ib_ah *ah = to_mah(wr->wr.ud.ah);
+ union ib_gid sgid;
u16 pkey;
int send_size;
int header_size;
int spc;
int i;
+ int is_eth;
+ int is_vlan = 0;
+ int is_grh;
+ u16 vlan;
send_size = 0;
for (i = 0; i < wr->num_sge; ++i)
send_size += wr->sg_list[i].length;
- ib_ud_header_init(send_size, mlx4_ib_ah_grh_present(ah), 0, &sqp->ud_header);
+ is_eth = rdma_port_get_link_layer(sqp->qp.ibqp.device, sqp->qp.port) == IB_LINK_LAYER_ETHERNET;
+ is_grh = mlx4_ib_ah_grh_present(ah);
+ if (is_eth) {
+ ib_get_cached_gid(ib_dev, be32_to_cpu(ah->av.ib.port_pd) >> 24,
+ ah->av.ib.gid_index, &sgid);
+ vlan = rdma_get_vlan_id(&sgid);
+ is_vlan = vlan < 0x1000;
+ }
+ ib_ud_header_init(send_size, !is_eth, is_eth, is_vlan, is_grh, 0, &sqp->ud_header);
+
+ if (!is_eth) {
+ sqp->ud_header.lrh.service_level =
+ be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28;
+ sqp->ud_header.lrh.destination_lid = ah->av.ib.dlid;
+ sqp->ud_header.lrh.source_lid = cpu_to_be16(ah->av.ib.g_slid & 0x7f);
+ }
- sqp->ud_header.lrh.service_level =
- be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 28;
- sqp->ud_header.lrh.destination_lid = ah->av.dlid;
- sqp->ud_header.lrh.source_lid = cpu_to_be16(ah->av.g_slid & 0x7f);
- if (mlx4_ib_ah_grh_present(ah)) {
+ if (is_grh) {
sqp->ud_header.grh.traffic_class =
- (be32_to_cpu(ah->av.sl_tclass_flowlabel) >> 20) & 0xff;
+ (be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 20) & 0xff;
sqp->ud_header.grh.flow_label =
- ah->av.sl_tclass_flowlabel & cpu_to_be32(0xfffff);
- sqp->ud_header.grh.hop_limit = ah->av.hop_limit;
- ib_get_cached_gid(ib_dev, be32_to_cpu(ah->av.port_pd) >> 24,
- ah->av.gid_index, &sqp->ud_header.grh.source_gid);
+ ah->av.ib.sl_tclass_flowlabel & cpu_to_be32(0xfffff);
+ sqp->ud_header.grh.hop_limit = ah->av.ib.hop_limit;
+ ib_get_cached_gid(ib_dev, be32_to_cpu(ah->av.ib.port_pd) >> 24,
+ ah->av.ib.gid_index, &sqp->ud_header.grh.source_gid);
memcpy(sqp->ud_header.grh.destination_gid.raw,
- ah->av.dgid, 16);
+ ah->av.ib.dgid, 16);
}
mlx->flags &= cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE);
- mlx->flags |= cpu_to_be32((!sqp->qp.ibqp.qp_num ? MLX4_WQE_MLX_VL15 : 0) |
- (sqp->ud_header.lrh.destination_lid ==
- IB_LID_PERMISSIVE ? MLX4_WQE_MLX_SLR : 0) |
- (sqp->ud_header.lrh.service_level << 8));
- mlx->rlid = sqp->ud_header.lrh.destination_lid;
+
+ if (!is_eth) {
+ mlx->flags |= cpu_to_be32((!sqp->qp.ibqp.qp_num ? MLX4_WQE_MLX_VL15 : 0) |
+ (sqp->ud_header.lrh.destination_lid ==
+ IB_LID_PERMISSIVE ? MLX4_WQE_MLX_SLR : 0) |
+ (sqp->ud_header.lrh.service_level << 8));
+ mlx->rlid = sqp->ud_header.lrh.destination_lid;
+ }
switch (wr->opcode) {
case IB_WR_SEND:
@@ -1270,9 +1360,29 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
return -EINVAL;
}
- sqp->ud_header.lrh.virtual_lane = !sqp->qp.ibqp.qp_num ? 15 : 0;
- if (sqp->ud_header.lrh.destination_lid == IB_LID_PERMISSIVE)
- sqp->ud_header.lrh.source_lid = IB_LID_PERMISSIVE;
+ if (is_eth) {
+ u8 *smac;
+
+ memcpy(sqp->ud_header.eth.dmac_h, ah->av.eth.mac, 6);
+ /* FIXME: cache smac value? */
+ smac = to_mdev(sqp->qp.ibqp.device)->iboe.netdevs[sqp->qp.port - 1]->dev_addr;
+ memcpy(sqp->ud_header.eth.smac_h, smac, 6);
+ if (!memcmp(sqp->ud_header.eth.smac_h, sqp->ud_header.eth.dmac_h, 6))
+ mlx->flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK);
+ if (!is_vlan) {
+ sqp->ud_header.eth.type = cpu_to_be16(MLX4_IB_IBOE_ETHERTYPE);
+ } else {
+ u16 pcp;
+
+ sqp->ud_header.vlan.type = cpu_to_be16(MLX4_IB_IBOE_ETHERTYPE);
+ pcp = (be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 27 & 3) << 13;
+ sqp->ud_header.vlan.tag = cpu_to_be16(vlan | pcp);
+ }
+ } else {
+ sqp->ud_header.lrh.virtual_lane = !sqp->qp.ibqp.qp_num ? 15 : 0;
+ if (sqp->ud_header.lrh.destination_lid == IB_LID_PERMISSIVE)
+ sqp->ud_header.lrh.source_lid = IB_LID_PERMISSIVE;
+ }
sqp->ud_header.bth.solicited_event = !!(wr->send_flags & IB_SEND_SOLICITED);
if (!sqp->qp.ibqp.qp_num)
ib_get_cached_pkey(ib_dev, sqp->qp.port, sqp->pkey_index, &pkey);
@@ -1429,11 +1539,14 @@ static void set_masked_atomic_seg(struct mlx4_wqe_masked_atomic_seg *aseg,
}
static void set_datagram_seg(struct mlx4_wqe_datagram_seg *dseg,
- struct ib_send_wr *wr)
+ struct ib_send_wr *wr, __be16 *vlan)
{
memcpy(dseg->av, &to_mah(wr->wr.ud.ah)->av, sizeof (struct mlx4_av));
dseg->dqpn = cpu_to_be32(wr->wr.ud.remote_qpn);
dseg->qkey = cpu_to_be32(wr->wr.ud.remote_qkey);
+ dseg->vlan = to_mah(wr->wr.ud.ah)->av.eth.vlan;
+ memcpy(dseg->mac, to_mah(wr->wr.ud.ah)->av.eth.mac, 6);
+ *vlan = dseg->vlan;
}
static void set_mlx_icrc_seg(void *dseg)
@@ -1536,6 +1649,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
__be32 uninitialized_var(lso_hdr_sz);
__be32 blh;
int i;
+ __be16 vlan = cpu_to_be16(0xffff);
spin_lock_irqsave(&qp->sq.lock, flags);
@@ -1639,7 +1753,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
break;
case IB_QPT_UD:
- set_datagram_seg(wqe, wr);
+ set_datagram_seg(wqe, wr, &vlan);
wqe += sizeof (struct mlx4_wqe_datagram_seg);
size += sizeof (struct mlx4_wqe_datagram_seg) / 16;
@@ -1717,6 +1831,11 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
ctrl->owner_opcode = mlx4_ib_opcode[wr->opcode] |
(ind & qp->sq.wqe_cnt ? cpu_to_be32(1 << 31) : 0) | blh;
+ if (be16_to_cpu(vlan) < 0x1000) {
+ ctrl->ins_vlan = 1 << 6;
+ ctrl->vlan_tag = vlan;
+ }
+
stamp = ind + qp->sq_spare_wqes;
ind += DIV_ROUND_UP(size * 16, 1U << qp->sq.wqe_shift);
@@ -1866,17 +1985,27 @@ static int to_ib_qp_access_flags(int mlx4_flags)
return ib_flags;
}
-static void to_ib_ah_attr(struct mlx4_dev *dev, struct ib_ah_attr *ib_ah_attr,
+static void to_ib_ah_attr(struct mlx4_ib_dev *ibdev, struct ib_ah_attr *ib_ah_attr,
struct mlx4_qp_path *path)
{
+ struct mlx4_dev *dev = ibdev->dev;
+ int is_eth;
+
memset(ib_ah_attr, 0, sizeof *ib_ah_attr);
ib_ah_attr->port_num = path->sched_queue & 0x40 ? 2 : 1;
if (ib_ah_attr->port_num == 0 || ib_ah_attr->port_num > dev->caps.num_ports)
return;
+ is_eth = rdma_port_get_link_layer(&ibdev->ib_dev, ib_ah_attr->port_num) ==
+ IB_LINK_LAYER_ETHERNET;
+ if (is_eth)
+ ib_ah_attr->sl = ((path->sched_queue >> 3) & 0x7) |
+ ((path->sched_queue & 4) << 1);
+ else
+ ib_ah_attr->sl = (path->sched_queue >> 2) & 0xf;
+
ib_ah_attr->dlid = be16_to_cpu(path->rlid);
- ib_ah_attr->sl = (path->sched_queue >> 2) & 0xf;
ib_ah_attr->src_path_bits = path->grh_mylmc & 0x7f;
ib_ah_attr->static_rate = path->static_rate ? path->static_rate - 5 : 0;
ib_ah_attr->ah_flags = (path->grh_mylmc & (1 << 7)) ? IB_AH_GRH : 0;
@@ -1929,8 +2058,8 @@ int mlx4_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr
to_ib_qp_access_flags(be32_to_cpu(context.params2));
if (qp->ibqp.qp_type == IB_QPT_RC || qp->ibqp.qp_type == IB_QPT_UC) {
- to_ib_ah_attr(dev->dev, &qp_attr->ah_attr, &context.pri_path);
- to_ib_ah_attr(dev->dev, &qp_attr->alt_ah_attr, &context.alt_path);
+ to_ib_ah_attr(dev, &qp_attr->ah_attr, &context.pri_path);
+ to_ib_ah_attr(dev, &qp_attr->alt_ah_attr, &context.alt_path);
qp_attr->alt_pkey_index = context.alt_path.pkey_index & 0x7f;
qp_attr->alt_port_num = qp_attr->alt_ah_attr.port_num;
}
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
index d2d172e6289c..a34c9d38e822 100644
--- a/drivers/infiniband/hw/mthca/mthca_qp.c
+++ b/drivers/infiniband/hw/mthca/mthca_qp.c
@@ -1493,7 +1493,7 @@ static int build_mlx_header(struct mthca_dev *dev, struct mthca_sqp *sqp,
int err;
u16 pkey;
- ib_ud_header_init(256, /* assume a MAD */
+ ib_ud_header_init(256, /* assume a MAD */ 1, 0, 0,
mthca_ah_grh_present(to_mah(wr->wr.ud.ah)), 0,
&sqp->ud_header);
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 6220d9d75b58..25ad0f9944c0 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -1424,7 +1424,6 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
{
int reset = 0; /* whether to send reset in case of err.. */
- int passive_state;
atomic_inc(&cm_resets_recvd);
nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u."
" refcnt=%d\n", cm_node, cm_node->state,
@@ -1439,7 +1438,7 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
active_open_err(cm_node, skb, reset);
break;
case NES_CM_STATE_MPAREQ_RCVD:
- passive_state = atomic_add_return(1, &cm_node->passive_state);
+ atomic_inc(&cm_node->passive_state);
dev_kfree_skb_any(skb);
break;
case NES_CM_STATE_ESTABLISHED:
diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
index 10560c796fd6..3892e2c0e95a 100644
--- a/drivers/infiniband/hw/nes/nes_nic.c
+++ b/drivers/infiniband/hw/nes/nes_nic.c
@@ -271,6 +271,7 @@ static int nes_netdev_stop(struct net_device *netdev)
if (netif_msg_ifdown(nesvnic))
printk(KERN_INFO PFX "%s: disabling interface\n", netdev->name);
+ netif_carrier_off(netdev);
/* Disable network packets */
napi_disable(&nesvnic->napi);
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 546fc22405fe..99933e4e48ff 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -476,9 +476,9 @@ static struct ib_fast_reg_page_list *nes_alloc_fast_reg_page_list(
}
nes_debug(NES_DBG_MR, "nes_alloc_fast_reg_pbl: nes_frpl = %p, "
"ibfrpl = %p, ibfrpl.page_list = %p, pbl.kva = %p, "
- "pbl.paddr= %p\n", pnesfrpl, &pnesfrpl->ibfrpl,
+ "pbl.paddr = %llx\n", pnesfrpl, &pnesfrpl->ibfrpl,
pnesfrpl->ibfrpl.page_list, pnesfrpl->nes_wqe_pbl.kva,
- (void *)pnesfrpl->nes_wqe_pbl.paddr);
+ (unsigned long long) pnesfrpl->nes_wqe_pbl.paddr);
return pifrpl;
}
@@ -584,7 +584,9 @@ static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr
props->lmc = 0;
props->sm_lid = 0;
props->sm_sl = 0;
- if (nesvnic->linkup)
+ if (netif_queue_stopped(netdev))
+ props->state = IB_PORT_DOWN;
+ else if (nesvnic->linkup)
props->state = IB_PORT_ACTIVE;
else
props->state = IB_PORT_DOWN;
@@ -3483,13 +3485,13 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr,
for (i = 0; i < ib_wr->wr.fast_reg.page_list_len; i++)
dst_page_list[i] = cpu_to_le64(src_page_list[i]);
- nes_debug(NES_DBG_IW_TX, "SQ_FMR: iova_start: %p, "
- "length: %d, rkey: %0x, pgl_paddr: %p, "
+ nes_debug(NES_DBG_IW_TX, "SQ_FMR: iova_start: %llx, "
+ "length: %d, rkey: %0x, pgl_paddr: %llx, "
"page_list_len: %u, wqe_misc: %x\n",
- (void *)ib_wr->wr.fast_reg.iova_start,
+ (unsigned long long) ib_wr->wr.fast_reg.iova_start,
ib_wr->wr.fast_reg.length,
ib_wr->wr.fast_reg.rkey,
- (void *)pnesfrpl->nes_wqe_pbl.paddr,
+ (unsigned long long) pnesfrpl->nes_wqe_pbl.paddr,
ib_wr->wr.fast_reg.page_list_len,
wqe_misc);
break;
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 61de0654820e..64c9e7d02d4a 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -1406,7 +1406,7 @@ extern struct mutex qib_mutex;
*/
#define qib_early_err(dev, fmt, ...) \
do { \
- dev_info(dev, KERN_ERR QIB_DRV_NAME ": " fmt, ##__VA_ARGS__); \
+ dev_err(dev, fmt, ##__VA_ARGS__); \
} while (0)
#define qib_dev_err(dd, fmt, ...) \
diff --git a/drivers/infiniband/hw/qib/qib_fs.c b/drivers/infiniband/hw/qib/qib_fs.c
index a0e6613e8be6..7e433d75c775 100644
--- a/drivers/infiniband/hw/qib/qib_fs.c
+++ b/drivers/infiniband/hw/qib/qib_fs.c
@@ -58,6 +58,7 @@ static int qibfs_mknod(struct inode *dir, struct dentry *dentry,
goto bail;
}
+ inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c
index f1d16d3a01f6..f3b503936043 100644
--- a/drivers/infiniband/hw/qib/qib_init.c
+++ b/drivers/infiniband/hw/qib/qib_init.c
@@ -1243,6 +1243,7 @@ static int __devinit qib_init_one(struct pci_dev *pdev,
qib_early_err(&pdev->dev, "QLogic PCIE device 0x%x cannot "
"work if CONFIG_PCI_MSI is not enabled\n",
ent->device);
+ dd = ERR_PTR(-ENODEV);
#endif
break;
diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c
index 7fa6e5592630..48b6674cbc49 100644
--- a/drivers/infiniband/hw/qib/qib_pcie.c
+++ b/drivers/infiniband/hw/qib/qib_pcie.c
@@ -103,16 +103,20 @@ int qib_pcie_init(struct pci_dev *pdev, const struct pci_device_id *ent)
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
} else
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
- if (ret)
+ if (ret) {
qib_early_err(&pdev->dev,
"Unable to set DMA consistent mask: %d\n", ret);
+ goto bail;
+ }
pci_set_master(pdev);
ret = pci_enable_pcie_error_reporting(pdev);
- if (ret)
+ if (ret) {
qib_early_err(&pdev->dev,
"Unable to enable pcie error reporting: %d\n",
ret);
+ ret = 0;
+ }
goto done;
bail:
diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index a0931119bd78..955fb7157793 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -2068,7 +2068,10 @@ send_last:
goto nack_op_err;
if (!ret)
goto rnr_nak;
- goto send_last_imm;
+ wc.ex.imm_data = ohdr->u.rc.imm_data;
+ hdrsize += 4;
+ wc.wc_flags = IB_WC_WITH_IMM;
+ goto send_last;
case OP(RDMA_READ_REQUEST): {
struct qib_ack_entry *e;
diff --git a/drivers/infiniband/hw/qib/qib_uc.c b/drivers/infiniband/hw/qib/qib_uc.c
index b9c8b6346c1b..32ccf3c824ca 100644
--- a/drivers/infiniband/hw/qib/qib_uc.c
+++ b/drivers/infiniband/hw/qib/qib_uc.c
@@ -457,8 +457,10 @@ rdma_first:
}
if (opcode == OP(RDMA_WRITE_ONLY))
goto rdma_last;
- else if (opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE))
+ else if (opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE)) {
+ wc.ex.imm_data = ohdr->u.rc.imm_data;
goto rdma_last_imm;
+ }
/* FALLTHROUGH */
case OP(RDMA_WRITE_MIDDLE):
/* Check for invalid length PMTU or posted rwqe len. */
@@ -471,8 +473,8 @@ rdma_first:
break;
case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE):
-rdma_last_imm:
wc.ex.imm_data = ohdr->u.imm_data;
+rdma_last_imm:
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index ec6b4fbe25e4..dfa71903d6e4 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -223,6 +223,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
unsigned int wr_id = wc->wr_id & ~IPOIB_OP_RECV;
struct sk_buff *skb;
u64 mapping[IPOIB_UD_RX_SG];
+ union ib_gid *dgid;
ipoib_dbg_data(priv, "recv completion: id %d, status: %d\n",
wr_id, wc->status);
@@ -271,6 +272,16 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
ipoib_ud_dma_unmap_rx(priv, mapping);
ipoib_ud_skb_put_frags(priv, skb, wc->byte_len);
+ /* First byte of dgid signals multicast when 0xff */
+ dgid = &((struct ib_grh *)skb->data)->dgid;
+
+ if (!(wc->wc_flags & IB_WC_GRH) || dgid->raw[0] != 0xff)
+ skb->pkt_type = PACKET_HOST;
+ else if (memcmp(dgid, dev->broadcast + 4, sizeof(union ib_gid)) == 0)
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_MULTICAST;
+
skb_pull(skb, IB_GRH_BYTES);
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
@@ -281,9 +292,6 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
dev->stats.rx_bytes += skb->len;
skb->dev = dev;
- /* XXX get correct PACKET_ type here */
- skb->pkt_type = PACKET_HOST;
-
if (test_bit(IPOIB_FLAG_CSUM, &priv->flags) && likely(wc->csum_ok))
skb->ip_summed = CHECKSUM_UNNECESSARY;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index b4b22576f12a..9ff7bc73ed95 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1240,6 +1240,7 @@ static struct net_device *ipoib_add_port(const char *format,
goto alloc_mem_failed;
SET_NETDEV_DEV(priv->dev, hca->dma_device);
+ priv->dev->dev_id = port - 1;
if (!ib_query_port(hca, port, &attr))
priv->max_ib_mtu = ib_mtu_enum_to_int(attr.max_mtu);
@@ -1362,6 +1363,8 @@ static void ipoib_add_one(struct ib_device *device)
}
for (p = s; p <= e; ++p) {
+ if (rdma_port_get_link_layer(device, p) != IB_LINK_LAYER_INFINIBAND)
+ continue;
dev = ipoib_add_port("ib%d", device, p);
if (!IS_ERR(dev)) {
priv = netdev_priv(dev);
@@ -1409,8 +1412,7 @@ static int __init ipoib_init_module(void)
ipoib_sendq_size = roundup_pow_of_two(ipoib_sendq_size);
ipoib_sendq_size = min(ipoib_sendq_size, IPOIB_MAX_QUEUE_SIZE);
- ipoib_sendq_size = max(ipoib_sendq_size, max(2 * MAX_SEND_CQE,
- IPOIB_MIN_QUEUE_SIZE));
+ ipoib_sendq_size = max3(ipoib_sendq_size, 2 * MAX_SEND_CQE, IPOIB_MIN_QUEUE_SIZE);
#ifdef CONFIG_INFINIBAND_IPOIB_CM
ipoib_max_conn_qp = min(ipoib_max_conn_qp, IPOIB_CM_MAX_CONN_QP);
#endif
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 7f8f16bad753..cfc1d65c4577 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -291,7 +291,7 @@ static void srp_free_target_ib(struct srp_target_port *target)
for (i = 0; i < SRP_RQ_SIZE; ++i)
srp_free_iu(target->srp_host, target->rx_ring[i]);
- for (i = 0; i < SRP_SQ_SIZE + 1; ++i)
+ for (i = 0; i < SRP_SQ_SIZE; ++i)
srp_free_iu(target->srp_host, target->tx_ring[i]);
}
@@ -811,6 +811,75 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
return len;
}
+/*
+ * Must be called with target->scsi_host->host_lock held to protect
+ * req_lim and tx_head. Lock cannot be dropped between call here and
+ * call to __srp_post_send().
+ *
+ * Note:
+ * An upper limit for the number of allocated information units for each
+ * request type is:
+ * - SRP_IU_CMD: SRP_CMD_SQ_SIZE, since the SCSI mid-layer never queues
+ * more than Scsi_Host.can_queue requests.
+ * - SRP_IU_TSK_MGMT: SRP_TSK_MGMT_SQ_SIZE.
+ * - SRP_IU_RSP: 1, since a conforming SRP target never sends more than
+ * one unanswered SRP request to an initiator.
+ */
+static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target,
+ enum srp_iu_type iu_type)
+{
+ s32 rsv = (iu_type == SRP_IU_TSK_MGMT) ? 0 : SRP_TSK_MGMT_SQ_SIZE;
+ struct srp_iu *iu;
+
+ srp_send_completion(target->send_cq, target);
+
+ if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE)
+ return NULL;
+
+ /* Initiator responses to target requests do not consume credits */
+ if (target->req_lim <= rsv && iu_type != SRP_IU_RSP) {
+ ++target->zero_req_lim;
+ return NULL;
+ }
+
+ iu = target->tx_ring[target->tx_head & SRP_SQ_MASK];
+ iu->type = iu_type;
+ return iu;
+}
+
+/*
+ * Must be called with target->scsi_host->host_lock held to protect
+ * req_lim and tx_head.
+ */
+static int __srp_post_send(struct srp_target_port *target,
+ struct srp_iu *iu, int len)
+{
+ struct ib_sge list;
+ struct ib_send_wr wr, *bad_wr;
+ int ret = 0;
+
+ list.addr = iu->dma;
+ list.length = len;
+ list.lkey = target->srp_host->srp_dev->mr->lkey;
+
+ wr.next = NULL;
+ wr.wr_id = target->tx_head & SRP_SQ_MASK;
+ wr.sg_list = &list;
+ wr.num_sge = 1;
+ wr.opcode = IB_WR_SEND;
+ wr.send_flags = IB_SEND_SIGNALED;
+
+ ret = ib_post_send(target->qp, &wr, &bad_wr);
+
+ if (!ret) {
+ ++target->tx_head;
+ if (iu->type != SRP_IU_RSP)
+ --target->req_lim;
+ }
+
+ return ret;
+}
+
static int srp_post_recv(struct srp_target_port *target)
{
unsigned long flags;
@@ -822,7 +891,7 @@ static int srp_post_recv(struct srp_target_port *target)
spin_lock_irqsave(target->scsi_host->host_lock, flags);
- next = target->rx_head & (SRP_RQ_SIZE - 1);
+ next = target->rx_head & SRP_RQ_MASK;
wr.wr_id = next;
iu = target->rx_ring[next];
@@ -896,6 +965,71 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
spin_unlock_irqrestore(target->scsi_host->host_lock, flags);
}
+static int srp_response_common(struct srp_target_port *target, s32 req_delta,
+ void *rsp, int len)
+{
+ struct ib_device *dev;
+ unsigned long flags;
+ struct srp_iu *iu;
+ int err = 1;
+
+ dev = target->srp_host->srp_dev->dev;
+
+ spin_lock_irqsave(target->scsi_host->host_lock, flags);
+ target->req_lim += req_delta;
+
+ iu = __srp_get_tx_iu(target, SRP_IU_RSP);
+ if (!iu) {
+ shost_printk(KERN_ERR, target->scsi_host, PFX
+ "no IU available to send response\n");
+ goto out;
+ }
+
+ ib_dma_sync_single_for_cpu(dev, iu->dma, len, DMA_TO_DEVICE);
+ memcpy(iu->buf, rsp, len);
+ ib_dma_sync_single_for_device(dev, iu->dma, len, DMA_TO_DEVICE);
+
+ err = __srp_post_send(target, iu, len);
+ if (err)
+ shost_printk(KERN_ERR, target->scsi_host, PFX
+ "unable to post response: %d\n", err);
+
+out:
+ spin_unlock_irqrestore(target->scsi_host->host_lock, flags);
+ return err;
+}
+
+static void srp_process_cred_req(struct srp_target_port *target,
+ struct srp_cred_req *req)
+{
+ struct srp_cred_rsp rsp = {
+ .opcode = SRP_CRED_RSP,
+ .tag = req->tag,
+ };
+ s32 delta = be32_to_cpu(req->req_lim_delta);
+
+ if (srp_response_common(target, delta, &rsp, sizeof rsp))
+ shost_printk(KERN_ERR, target->scsi_host, PFX
+ "problems processing SRP_CRED_REQ\n");
+}
+
+static void srp_process_aer_req(struct srp_target_port *target,
+ struct srp_aer_req *req)
+{
+ struct srp_aer_rsp rsp = {
+ .opcode = SRP_AER_RSP,
+ .tag = req->tag,
+ };
+ s32 delta = be32_to_cpu(req->req_lim_delta);
+
+ shost_printk(KERN_ERR, target->scsi_host, PFX
+ "ignoring AER for LUN %llu\n", be64_to_cpu(req->lun));
+
+ if (srp_response_common(target, delta, &rsp, sizeof rsp))
+ shost_printk(KERN_ERR, target->scsi_host, PFX
+ "problems processing SRP_AER_REQ\n");
+}
+
static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
{
struct ib_device *dev;
@@ -923,6 +1057,14 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
srp_process_rsp(target, iu->buf);
break;
+ case SRP_CRED_REQ:
+ srp_process_cred_req(target, iu->buf);
+ break;
+
+ case SRP_AER_REQ:
+ srp_process_aer_req(target, iu->buf);
+ break;
+
case SRP_T_LOGOUT:
/* XXX Handle target logout */
shost_printk(KERN_WARNING, target->scsi_host,
@@ -981,61 +1123,6 @@ static void srp_send_completion(struct ib_cq *cq, void *target_ptr)
}
}
-/*
- * Must be called with target->scsi_host->host_lock held to protect
- * req_lim and tx_head. Lock cannot be dropped between call here and
- * call to __srp_post_send().
- */
-static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target,
- enum srp_request_type req_type)
-{
- s32 min = (req_type == SRP_REQ_TASK_MGMT) ? 1 : 2;
-
- srp_send_completion(target->send_cq, target);
-
- if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE)
- return NULL;
-
- if (target->req_lim < min) {
- ++target->zero_req_lim;
- return NULL;
- }
-
- return target->tx_ring[target->tx_head & SRP_SQ_SIZE];
-}
-
-/*
- * Must be called with target->scsi_host->host_lock held to protect
- * req_lim and tx_head.
- */
-static int __srp_post_send(struct srp_target_port *target,
- struct srp_iu *iu, int len)
-{
- struct ib_sge list;
- struct ib_send_wr wr, *bad_wr;
- int ret = 0;
-
- list.addr = iu->dma;
- list.length = len;
- list.lkey = target->srp_host->srp_dev->mr->lkey;
-
- wr.next = NULL;
- wr.wr_id = target->tx_head & SRP_SQ_SIZE;
- wr.sg_list = &list;
- wr.num_sge = 1;
- wr.opcode = IB_WR_SEND;
- wr.send_flags = IB_SEND_SIGNALED;
-
- ret = ib_post_send(target->qp, &wr, &bad_wr);
-
- if (!ret) {
- ++target->tx_head;
- --target->req_lim;
- }
-
- return ret;
-}
-
static int srp_queuecommand(struct scsi_cmnd *scmnd,
void (*done)(struct scsi_cmnd *))
{
@@ -1056,7 +1143,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
return 0;
}
- iu = __srp_get_tx_iu(target, SRP_REQ_NORMAL);
+ iu = __srp_get_tx_iu(target, SRP_IU_CMD);
if (!iu)
goto err;
@@ -1064,7 +1151,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
ib_dma_sync_single_for_cpu(dev, iu->dma, srp_max_iu_len,
DMA_TO_DEVICE);
- req = list_entry(target->free_reqs.next, struct srp_request, list);
+ req = list_first_entry(&target->free_reqs, struct srp_request, list);
scmnd->scsi_done = done;
scmnd->result = 0;
@@ -1121,7 +1208,7 @@ static int srp_alloc_iu_bufs(struct srp_target_port *target)
goto err;
}
- for (i = 0; i < SRP_SQ_SIZE + 1; ++i) {
+ for (i = 0; i < SRP_SQ_SIZE; ++i) {
target->tx_ring[i] = srp_alloc_iu(target->srp_host,
srp_max_iu_len,
GFP_KERNEL, DMA_TO_DEVICE);
@@ -1137,7 +1224,7 @@ err:
target->rx_ring[i] = NULL;
}
- for (i = 0; i < SRP_SQ_SIZE + 1; ++i) {
+ for (i = 0; i < SRP_SQ_SIZE; ++i) {
srp_free_iu(target->srp_host, target->tx_ring[i]);
target->tx_ring[i] = NULL;
}
@@ -1252,8 +1339,13 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
target->max_ti_iu_len = be32_to_cpu(rsp->max_ti_iu_len);
target->req_lim = be32_to_cpu(rsp->req_lim_delta);
- target->scsi_host->can_queue = min(target->req_lim,
- target->scsi_host->can_queue);
+ /*
+ * Reserve credits for task management so we don't
+ * bounce requests back to the SCSI mid-layer.
+ */
+ target->scsi_host->can_queue
+ = min(target->req_lim - SRP_TSK_MGMT_SQ_SIZE,
+ target->scsi_host->can_queue);
} else {
shost_printk(KERN_WARNING, target->scsi_host,
PFX "Unhandled RSP opcode %#x\n", opcode);
@@ -1350,6 +1442,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
static int srp_send_tsk_mgmt(struct srp_target_port *target,
struct srp_request *req, u8 func)
{
+ struct ib_device *dev = target->srp_host->srp_dev->dev;
struct srp_iu *iu;
struct srp_tsk_mgmt *tsk_mgmt;
@@ -1363,10 +1456,12 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
init_completion(&req->done);
- iu = __srp_get_tx_iu(target, SRP_REQ_TASK_MGMT);
+ iu = __srp_get_tx_iu(target, SRP_IU_TSK_MGMT);
if (!iu)
goto out;
+ ib_dma_sync_single_for_cpu(dev, iu->dma, sizeof *tsk_mgmt,
+ DMA_TO_DEVICE);
tsk_mgmt = iu->buf;
memset(tsk_mgmt, 0, sizeof *tsk_mgmt);
@@ -1376,6 +1471,8 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
tsk_mgmt->tsk_mgmt_func = func;
tsk_mgmt->task_tag = req->index;
+ ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt,
+ DMA_TO_DEVICE);
if (__srp_post_send(target, iu, sizeof *tsk_mgmt))
goto out;
@@ -1626,9 +1723,9 @@ static struct scsi_host_template srp_template = {
.eh_abort_handler = srp_abort,
.eh_device_reset_handler = srp_reset_device,
.eh_host_reset_handler = srp_reset_host,
- .can_queue = SRP_SQ_SIZE,
+ .can_queue = SRP_CMD_SQ_SIZE,
.this_id = -1,
- .cmd_per_lun = SRP_SQ_SIZE,
+ .cmd_per_lun = SRP_CMD_SQ_SIZE,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = srp_host_attrs
};
@@ -1813,7 +1910,7 @@ static int srp_parse_options(const char *buf, struct srp_target_port *target)
printk(KERN_WARNING PFX "bad max cmd_per_lun parameter '%s'\n", p);
goto out;
}
- target->scsi_host->cmd_per_lun = min(token, SRP_SQ_SIZE);
+ target->scsi_host->cmd_per_lun = min(token, SRP_CMD_SQ_SIZE);
break;
case SRP_OPT_IO_CLASS:
@@ -1891,7 +1988,7 @@ static ssize_t srp_create_target(struct device *dev,
INIT_LIST_HEAD(&target->free_reqs);
INIT_LIST_HEAD(&target->req_queue);
- for (i = 0; i < SRP_SQ_SIZE; ++i) {
+ for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) {
target->req_ring[i].index = i;
list_add_tail(&target->req_ring[i].list, &target->free_reqs);
}
@@ -2159,6 +2256,9 @@ static int __init srp_init_module(void)
{
int ret;
+ BUILD_BUG_ON_NOT_POWER_OF_2(SRP_SQ_SIZE);
+ BUILD_BUG_ON_NOT_POWER_OF_2(SRP_RQ_SIZE);
+
if (srp_sg_tablesize > 255) {
printk(KERN_WARNING PFX "Clamping srp_sg_tablesize to 255\n");
srp_sg_tablesize = 255;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 5a80eac6fdaa..ed0dce9e479f 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -59,7 +59,14 @@ enum {
SRP_RQ_SHIFT = 6,
SRP_RQ_SIZE = 1 << SRP_RQ_SHIFT,
- SRP_SQ_SIZE = SRP_RQ_SIZE - 1,
+ SRP_RQ_MASK = SRP_RQ_SIZE - 1,
+
+ SRP_SQ_SIZE = SRP_RQ_SIZE,
+ SRP_SQ_MASK = SRP_SQ_SIZE - 1,
+ SRP_RSP_SQ_SIZE = 1,
+ SRP_REQ_SQ_SIZE = SRP_SQ_SIZE - SRP_RSP_SQ_SIZE,
+ SRP_TSK_MGMT_SQ_SIZE = 1,
+ SRP_CMD_SQ_SIZE = SRP_REQ_SQ_SIZE - SRP_TSK_MGMT_SQ_SIZE,
SRP_TAG_TSK_MGMT = 1 << (SRP_RQ_SHIFT + 1),
@@ -75,9 +82,10 @@ enum srp_target_state {
SRP_TARGET_REMOVED
};
-enum srp_request_type {
- SRP_REQ_NORMAL,
- SRP_REQ_TASK_MGMT,
+enum srp_iu_type {
+ SRP_IU_CMD,
+ SRP_IU_TSK_MGMT,
+ SRP_IU_RSP,
};
struct srp_device {
@@ -144,11 +152,11 @@ struct srp_target_port {
unsigned tx_head;
unsigned tx_tail;
- struct srp_iu *tx_ring[SRP_SQ_SIZE + 1];
+ struct srp_iu *tx_ring[SRP_SQ_SIZE];
struct list_head free_reqs;
struct list_head req_queue;
- struct srp_request req_ring[SRP_SQ_SIZE];
+ struct srp_request req_ring[SRP_CMD_SQ_SIZE];
struct work_struct work;
@@ -164,6 +172,7 @@ struct srp_iu {
void *buf;
size_t size;
enum dma_data_direction direction;
+ enum srp_iu_type type;
};
#endif /* IB_SRP_H */
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 535fea4fe67f..e3f7fc6f9565 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -534,6 +534,80 @@ static int handle_eviocgbit(struct input_dev *dev,
}
#undef OLD_KEY_MAX
+static int evdev_handle_get_keycode(struct input_dev *dev,
+ void __user *p, size_t size)
+{
+ struct input_keymap_entry ke;
+ int error;
+
+ memset(&ke, 0, sizeof(ke));
+
+ if (size == sizeof(unsigned int[2])) {
+ /* legacy case */
+ int __user *ip = (int __user *)p;
+
+ if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+ return -EFAULT;
+
+ ke.len = sizeof(unsigned int);
+ ke.flags = 0;
+
+ error = input_get_keycode(dev, &ke);
+ if (error)
+ return error;
+
+ if (put_user(ke.keycode, ip + 1))
+ return -EFAULT;
+
+ } else {
+ size = min(size, sizeof(ke));
+
+ if (copy_from_user(&ke, p, size))
+ return -EFAULT;
+
+ error = input_get_keycode(dev, &ke);
+ if (error)
+ return error;
+
+ if (copy_to_user(p, &ke, size))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int evdev_handle_set_keycode(struct input_dev *dev,
+ void __user *p, size_t size)
+{
+ struct input_keymap_entry ke;
+
+ memset(&ke, 0, sizeof(ke));
+
+ if (size == sizeof(unsigned int[2])) {
+ /* legacy case */
+ int __user *ip = (int __user *)p;
+
+ if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+ return -EFAULT;
+
+ if (get_user(ke.keycode, ip + 1))
+ return -EFAULT;
+
+ ke.len = sizeof(unsigned int);
+ ke.flags = 0;
+
+ } else {
+ size = min(size, sizeof(ke));
+
+ if (copy_from_user(&ke, p, size))
+ return -EFAULT;
+
+ if (ke.len > sizeof(ke.scancode))
+ return -EINVAL;
+ }
+
+ return input_set_keycode(dev, &ke);
+}
+
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
@@ -580,25 +654,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return 0;
- case EVIOCGKEYCODE:
- if (get_user(t, ip))
- return -EFAULT;
-
- error = input_get_keycode(dev, t, &v);
- if (error)
- return error;
-
- if (put_user(v, ip + 1))
- return -EFAULT;
-
- return 0;
-
- case EVIOCSKEYCODE:
- if (get_user(t, ip) || get_user(v, ip + 1))
- return -EFAULT;
-
- return input_set_keycode(dev, t, v);
-
case EVIOCRMFF:
return input_ff_erase(dev, (int)(unsigned long) p, file);
@@ -620,7 +675,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
/* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
-
switch (EVIOC_MASK_SIZE(cmd)) {
case EVIOCGKEY(0):
@@ -654,6 +708,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return -EFAULT;
return error;
+
+ case EVIOC_MASK_SIZE(EVIOCGKEYCODE):
+ return evdev_handle_get_keycode(dev, p, size);
+
+ case EVIOC_MASK_SIZE(EVIOCSKEYCODE):
+ return evdev_handle_set_keycode(dev, p, size);
}
/* Multi-number variable-length handlers */
diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c
index 7392992da424..422aa0a6b77f 100644
--- a/drivers/input/gameport/emu10k1-gp.c
+++ b/drivers/input/gameport/emu10k1-gp.c
@@ -59,44 +59,52 @@ MODULE_DEVICE_TABLE(pci, emu_tbl);
static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- int ioport, iolen;
struct emu *emu;
struct gameport *port;
-
- if (pci_enable_device(pdev))
- return -EBUSY;
-
- ioport = pci_resource_start(pdev, 0);
- iolen = pci_resource_len(pdev, 0);
-
- if (!request_region(ioport, iolen, "emu10k1-gp"))
- return -EBUSY;
+ int error;
emu = kzalloc(sizeof(struct emu), GFP_KERNEL);
port = gameport_allocate_port();
if (!emu || !port) {
printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
- release_region(ioport, iolen);
- kfree(emu);
- gameport_free_port(port);
- return -ENOMEM;
+ error = -ENOMEM;
+ goto err_out_free;
}
- emu->io = ioport;
- emu->size = iolen;
+ error = pci_enable_device(pdev);
+ if (error)
+ goto err_out_free;
+
+ emu->io = pci_resource_start(pdev, 0);
+ emu->size = pci_resource_len(pdev, 0);
+
emu->dev = pdev;
emu->gameport = port;
gameport_set_name(port, "EMU10K1");
gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
port->dev.parent = &pdev->dev;
- port->io = ioport;
+ port->io = emu->io;
+
+ if (!request_region(emu->io, emu->size, "emu10k1-gp")) {
+ printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n",
+ emu->io, emu->io + emu->size - 1);
+ error = -EBUSY;
+ goto err_out_disable_dev;
+ }
pci_set_drvdata(pdev, emu);
gameport_register_port(port);
return 0;
+
+ err_out_disable_dev:
+ pci_disable_device(pdev);
+ err_out_free:
+ gameport_free_port(port);
+ kfree(emu);
+ return error;
}
static void __devexit emu_remove(struct pci_dev *pdev)
@@ -106,6 +114,8 @@ static void __devexit emu_remove(struct pci_dev *pdev)
gameport_unregister_port(emu->gameport);
release_region(emu->io, emu->size);
kfree(emu);
+
+ pci_disable_device(pdev);
}
static struct pci_driver emu_driver = {
diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c
index 14d3f3e208a2..a3b70ff21018 100644
--- a/drivers/input/gameport/fm801-gp.c
+++ b/drivers/input/gameport/fm801-gp.c
@@ -133,11 +133,11 @@ static void __devexit fm801_gp_remove(struct pci_dev *pci)
{
struct fm801_gp *gp = pci_get_drvdata(pci);
- if (gp) {
- gameport_unregister_port(gp->gameport);
- release_resource(gp->res_port);
- kfree(gp);
- }
+ gameport_unregister_port(gp->gameport);
+ release_resource(gp->res_port);
+ kfree(gp);
+
+ pci_disable_device(pci);
}
static const struct pci_device_id fm801_gp_id_table[] = {
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 7919c2537225..d092ef9291da 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -171,7 +171,7 @@ static int input_handle_abs_event(struct input_dev *dev,
if (code == ABS_MT_SLOT) {
/*
* "Stage" the event; we'll flush it later, when we
- * get actiual touch data.
+ * get actual touch data.
*/
if (*pval >= 0 && *pval < dev->mtsize)
dev->slot = *pval;
@@ -188,7 +188,7 @@ static int input_handle_abs_event(struct input_dev *dev,
pold = &mtslot->abs[code - ABS_MT_FIRST];
} else {
/*
- * Bypass filtering for multitouch events when
+ * Bypass filtering for multi-touch events when
* not employing slots.
*/
pold = NULL;
@@ -634,78 +634,141 @@ static void input_disconnect_device(struct input_dev *dev)
spin_unlock_irq(&dev->event_lock);
}
-static int input_fetch_keycode(struct input_dev *dev, int scancode)
+/**
+ * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry
+ * @ke: keymap entry containing scancode to be converted.
+ * @scancode: pointer to the location where converted scancode should
+ * be stored.
+ *
+ * This function is used to convert scancode stored in &struct keymap_entry
+ * into scalar form understood by legacy keymap handling methods. These
+ * methods expect scancodes to be represented as 'unsigned int'.
+ */
+int input_scancode_to_scalar(const struct input_keymap_entry *ke,
+ unsigned int *scancode)
+{
+ switch (ke->len) {
+ case 1:
+ *scancode = *((u8 *)ke->scancode);
+ break;
+
+ case 2:
+ *scancode = *((u16 *)ke->scancode);
+ break;
+
+ case 4:
+ *scancode = *((u32 *)ke->scancode);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(input_scancode_to_scalar);
+
+/*
+ * Those routines handle the default case where no [gs]etkeycode() is
+ * defined. In this case, an array indexed by the scancode is used.
+ */
+
+static unsigned int input_fetch_keycode(struct input_dev *dev,
+ unsigned int index)
{
switch (dev->keycodesize) {
- case 1:
- return ((u8 *)dev->keycode)[scancode];
+ case 1:
+ return ((u8 *)dev->keycode)[index];
- case 2:
- return ((u16 *)dev->keycode)[scancode];
+ case 2:
+ return ((u16 *)dev->keycode)[index];
- default:
- return ((u32 *)dev->keycode)[scancode];
+ default:
+ return ((u32 *)dev->keycode)[index];
}
}
static int input_default_getkeycode(struct input_dev *dev,
- unsigned int scancode,
- unsigned int *keycode)
+ struct input_keymap_entry *ke)
{
+ unsigned int index;
+ int error;
+
if (!dev->keycodesize)
return -EINVAL;
- if (scancode >= dev->keycodemax)
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+ index = ke->index;
+ else {
+ error = input_scancode_to_scalar(ke, &index);
+ if (error)
+ return error;
+ }
+
+ if (index >= dev->keycodemax)
return -EINVAL;
- *keycode = input_fetch_keycode(dev, scancode);
+ ke->keycode = input_fetch_keycode(dev, index);
+ ke->index = index;
+ ke->len = sizeof(index);
+ memcpy(ke->scancode, &index, sizeof(index));
return 0;
}
static int input_default_setkeycode(struct input_dev *dev,
- unsigned int scancode,
- unsigned int keycode)
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
{
- int old_keycode;
+ unsigned int index;
+ int error;
int i;
- if (scancode >= dev->keycodemax)
+ if (!dev->keycodesize)
return -EINVAL;
- if (!dev->keycodesize)
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+ index = ke->index;
+ } else {
+ error = input_scancode_to_scalar(ke, &index);
+ if (error)
+ return error;
+ }
+
+ if (index >= dev->keycodemax)
return -EINVAL;
- if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8)))
+ if (dev->keycodesize < sizeof(dev->keycode) &&
+ (ke->keycode >> (dev->keycodesize * 8)))
return -EINVAL;
switch (dev->keycodesize) {
case 1: {
u8 *k = (u8 *)dev->keycode;
- old_keycode = k[scancode];
- k[scancode] = keycode;
+ *old_keycode = k[index];
+ k[index] = ke->keycode;
break;
}
case 2: {
u16 *k = (u16 *)dev->keycode;
- old_keycode = k[scancode];
- k[scancode] = keycode;
+ *old_keycode = k[index];
+ k[index] = ke->keycode;
break;
}
default: {
u32 *k = (u32 *)dev->keycode;
- old_keycode = k[scancode];
- k[scancode] = keycode;
+ *old_keycode = k[index];
+ k[index] = ke->keycode;
break;
}
}
- __clear_bit(old_keycode, dev->keybit);
- __set_bit(keycode, dev->keybit);
+ __clear_bit(*old_keycode, dev->keybit);
+ __set_bit(ke->keycode, dev->keybit);
for (i = 0; i < dev->keycodemax; i++) {
- if (input_fetch_keycode(dev, i) == old_keycode) {
- __set_bit(old_keycode, dev->keybit);
+ if (input_fetch_keycode(dev, i) == *old_keycode) {
+ __set_bit(*old_keycode, dev->keybit);
break; /* Setting the bit twice is useless, so break */
}
}
@@ -716,53 +779,86 @@ static int input_default_setkeycode(struct input_dev *dev,
/**
* input_get_keycode - retrieve keycode currently mapped to a given scancode
* @dev: input device which keymap is being queried
- * @scancode: scancode (or its equivalent for device in question) for which
- * keycode is needed
- * @keycode: result
+ * @ke: keymap entry
*
* This function should be called by anyone interested in retrieving current
- * keymap. Presently keyboard and evdev handlers use it.
+ * keymap. Presently evdev handlers use it.
*/
-int input_get_keycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
+int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke)
{
unsigned long flags;
int retval;
spin_lock_irqsave(&dev->event_lock, flags);
- retval = dev->getkeycode(dev, scancode, keycode);
- spin_unlock_irqrestore(&dev->event_lock, flags);
+ if (dev->getkeycode) {
+ /*
+ * Support for legacy drivers, that don't implement the new
+ * ioctls
+ */
+ u32 scancode = ke->index;
+
+ memcpy(ke->scancode, &scancode, sizeof(scancode));
+ ke->len = sizeof(scancode);
+ retval = dev->getkeycode(dev, scancode, &ke->keycode);
+ } else {
+ retval = dev->getkeycode_new(dev, ke);
+ }
+
+ spin_unlock_irqrestore(&dev->event_lock, flags);
return retval;
}
EXPORT_SYMBOL(input_get_keycode);
/**
- * input_get_keycode - assign new keycode to a given scancode
+ * input_set_keycode - attribute a keycode to a given scancode
* @dev: input device which keymap is being updated
- * @scancode: scancode (or its equivalent for device in question)
- * @keycode: new keycode to be assigned to the scancode
+ * @ke: new keymap entry
*
* This function should be called by anyone needing to update current
* keymap. Presently keyboard and evdev handlers use it.
*/
int input_set_keycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
+ const struct input_keymap_entry *ke)
{
unsigned long flags;
unsigned int old_keycode;
int retval;
- if (keycode > KEY_MAX)
+ if (ke->keycode > KEY_MAX)
return -EINVAL;
spin_lock_irqsave(&dev->event_lock, flags);
- retval = dev->getkeycode(dev, scancode, &old_keycode);
- if (retval)
- goto out;
+ if (dev->setkeycode) {
+ /*
+ * Support for legacy drivers, that don't implement the new
+ * ioctls
+ */
+ unsigned int scancode;
+
+ retval = input_scancode_to_scalar(ke, &scancode);
+ if (retval)
+ goto out;
+
+ /*
+ * We need to know the old scancode, in order to generate a
+ * keyup effect, if the set operation happens successfully
+ */
+ if (!dev->getkeycode) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ retval = dev->getkeycode(dev, scancode, &old_keycode);
+ if (retval)
+ goto out;
+
+ retval = dev->setkeycode(dev, scancode, ke->keycode);
+ } else {
+ retval = dev->setkeycode_new(dev, ke, &old_keycode);
+ }
- retval = dev->setkeycode(dev, scancode, keycode);
if (retval)
goto out;
@@ -1601,7 +1697,7 @@ EXPORT_SYMBOL(input_free_device);
*
* This function allocates all necessary memory for MT slot handling in the
* input device, and adds ABS_MT_SLOT to the device capabilities. All slots
- * are initially marked as unused iby setting ABS_MT_TRACKING_ID to -1.
+ * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1.
*/
int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots)
{
@@ -1759,11 +1855,11 @@ int input_register_device(struct input_dev *dev)
dev->rep[REP_PERIOD] = 33;
}
- if (!dev->getkeycode)
- dev->getkeycode = input_default_getkeycode;
+ if (!dev->getkeycode && !dev->getkeycode_new)
+ dev->getkeycode_new = input_default_getkeycode;
- if (!dev->setkeycode)
- dev->setkeycode = input_default_setkeycode;
+ if (!dev->setkeycode && !dev->setkeycode_new)
+ dev->setkeycode_new = input_default_setkeycode;
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index aa037fec2f86..b8c51b9781db 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
+config KEYBOARD_NOMADIK
+ tristate "ST-Ericsson Nomadik SKE keyboard"
+ depends on PLAT_NOMADIK
+ help
+ Say Y here if you want to use a keypad provided on the SKE controller
+ used on the Ux500 and Nomadik platforms
+
+ To compile this driver as a module, choose M here: the
+ module will be called nmk-ske-keypad.
+
config KEYBOARD_OPENCORES
tristate "OpenCores Keyboard Controller"
help
@@ -424,6 +434,24 @@ config KEYBOARD_OMAP
To compile this driver as a module, choose M here: the
module will be called omap-keypad.
+config KEYBOARD_OMAP4
+ tristate "TI OMAP4 keypad support"
+ depends on ARCH_OMAP4
+ help
+ Say Y here if you want to use the OMAP4 keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap4-keypad.
+
+config KEYBOARD_TNETV107X
+ tristate "TI TNETV107X keypad support"
+ depends on ARCH_DAVINCI_TNETV107X
+ help
+ Say Y here if you want to use the TNETV107X keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tnetv107x-keypad.
+
config KEYBOARD_TWL4030
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
depends on TWL4030_CORE
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 504b591be0cd..a34452e8ebe2 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -28,7 +28,9 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
@@ -38,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
+obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
index d6918cb966c0..b92d1cd5cba1 100644
--- a/drivers/input/keyboard/adp5588-keys.c
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = {
#endif
static const struct i2c_device_id adp5588_id[] = {
- { KBUILD_MODNAME, 0 },
+ { "adp5588-keys", 0 },
{ "adp5587-keys", 0 },
{ }
};
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index 19fa94af207a..fed31e0947a1 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -570,6 +570,8 @@ static struct serio_device_id hil_dev_ids[] = {
{ 0 }
};
+MODULE_DEVICE_TABLE(serio, hil_dev_ids);
+
static struct serio_driver hil_serio_drv = {
.driver = {
.name = "hil_dev",
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
new file mode 100644
index 000000000000..6e0f23091360
--- /dev/null
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * License terms:GNU General Public License (GPL) version 2
+ *
+ * Keypad controller driver for the SKE (Scroll Key Encoder) module used in
+ * the Nomadik 8815 and Ux500 platforms.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <plat/ske.h>
+
+/* SKE_CR bits */
+#define SKE_KPMLT (0x1 << 6)
+#define SKE_KPCN (0x7 << 3)
+#define SKE_KPASEN (0x1 << 2)
+#define SKE_KPASON (0x1 << 7)
+
+/* SKE_IMSC bits */
+#define SKE_KPIMA (0x1 << 2)
+
+/* SKE_ICR bits */
+#define SKE_KPICS (0x1 << 3)
+#define SKE_KPICA (0x1 << 2)
+
+/* SKE_RIS bits */
+#define SKE_KPRISA (0x1 << 2)
+
+#define SKE_KEYPAD_ROW_SHIFT 3
+#define SKE_KPD_KEYMAP_SIZE (8 * 8)
+
+/* keypad auto scan registers */
+#define SKE_ASR0 0x20
+#define SKE_ASR1 0x24
+#define SKE_ASR2 0x28
+#define SKE_ASR3 0x2C
+
+#define SKE_NUM_ASRX_REGISTERS (4)
+
+/**
+ * struct ske_keypad - data structure used by keypad driver
+ * @irq: irq no
+ * @reg_base: ske regsiters base address
+ * @input: pointer to input device object
+ * @board: keypad platform device
+ * @keymap: matrix scan code table for keycodes
+ * @clk: clock structure pointer
+ */
+struct ske_keypad {
+ int irq;
+ void __iomem *reg_base;
+ struct input_dev *input;
+ const struct ske_keypad_platform_data *board;
+ unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
+ struct clk *clk;
+ spinlock_t ske_keypad_lock;
+};
+
+static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
+ u8 mask, u8 data)
+{
+ u32 ret;
+
+ spin_lock(&keypad->ske_keypad_lock);
+
+ ret = readl(keypad->reg_base + addr);
+ ret &= ~mask;
+ ret |= data;
+ writel(ret, keypad->reg_base + addr);
+
+ spin_unlock(&keypad->ske_keypad_lock);
+}
+
+/*
+ * ske_keypad_chip_init: init keypad controller configuration
+ *
+ * Enable Multi key press detection, auto scan mode
+ */
+static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
+{
+ u32 value;
+ int timeout = 50;
+
+ /* check SKE_RIS to be 0 */
+ while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
+ cpu_relax();
+
+ if (!timeout)
+ return -EINVAL;
+
+ /*
+ * set debounce value
+ * keypad dbounce is configured in DBCR[15:8]
+ * dbounce value in steps of 32/32.768 ms
+ */
+ spin_lock(&keypad->ske_keypad_lock);
+ value = readl(keypad->reg_base + SKE_DBCR);
+ value = value & 0xff;
+ value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
+ writel(value, keypad->reg_base + SKE_DBCR);
+ spin_unlock(&keypad->ske_keypad_lock);
+
+ /* enable multi key detection */
+ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
+
+ /*
+ * set up the number of columns
+ * KPCN[5:3] defines no. of keypad columns to be auto scanned
+ */
+ value = (keypad->board->kcol - 1) << 3;
+ ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
+
+ /* clear keypad interrupt for auto(and pending SW) scans */
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
+
+ /* un-mask keypad interrupts */
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+ /* enable automatic scan */
+ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
+
+ return 0;
+}
+
+static void ske_keypad_read_data(struct ske_keypad *keypad)
+{
+ struct input_dev *input = keypad->input;
+ u16 status;
+ int col = 0, row = 0, code;
+ int ske_asr, ske_ris, key_pressed, i;
+
+ /*
+ * Read the auto scan registers
+ *
+ * Each SKE_ASRx (x=0 to x=3) contains two row values.
+ * lower byte contains row value for column 2*x,
+ * upper byte contains row value for column 2*x + 1
+ */
+ for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
+ ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
+ if (!ske_asr)
+ continue;
+
+ /* now that ASRx is zero, find out the column x and row y*/
+ if (ske_asr & 0xff) {
+ col = i * 2;
+ status = ske_asr & 0xff;
+ } else {
+ col = (i * 2) + 1;
+ status = (ske_asr & 0xff00) >> 8;
+ }
+
+ /* find out the row */
+ row = __ffs(status);
+
+ code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
+ ske_ris = readl(keypad->reg_base + SKE_RIS);
+ key_pressed = ske_ris & SKE_KPRISA;
+
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code], key_pressed);
+ input_sync(input);
+ }
+}
+
+static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
+{
+ struct ske_keypad *keypad = dev_id;
+ int retries = 20;
+
+ /* disable auto scan interrupt; mask the interrupt generated */
+ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+
+ while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)
+ msleep(5);
+
+ if (retries) {
+ /* SKEx registers are stable and can be read */
+ ske_keypad_read_data(keypad);
+ }
+
+ /* enable auto scan interrupts */
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ske_keypad_probe(struct platform_device *pdev)
+{
+ const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
+ struct ske_keypad *keypad;
+ struct input_dev *input;
+ struct resource *res;
+ int irq;
+ int error;
+
+ if (!plat) {
+ dev_err(&pdev->dev, "invalid keypad platform data\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keypad irq\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing platform resources\n");
+ return -EINVAL;
+ }
+
+ keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!keypad || !input) {
+ dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ keypad->irq = irq;
+ keypad->board = plat;
+ keypad->input = input;
+ spin_lock_init(&keypad->ske_keypad_lock);
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ keypad->reg_base = ioremap(res->start, resource_size(res));
+ if (!keypad->reg_base) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto err_free_mem_region;
+ }
+
+ keypad->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(keypad->clk)) {
+ dev_err(&pdev->dev, "failed to get clk\n");
+ error = PTR_ERR(keypad->clk);
+ goto err_iounmap;
+ }
+
+ input->id.bustype = BUS_HOST;
+ input->name = "ux500-ske-keypad";
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ clk_enable(keypad->clk);
+
+ /* go through board initialization helpers */
+ if (keypad->board->init)
+ keypad->board->init();
+
+ error = ske_keypad_chip_init(keypad);
+ if (error) {
+ dev_err(&pdev->dev, "unable to init keypad hardware\n");
+ goto err_clk_disable;
+ }
+
+ error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
+ IRQF_ONESHOT, "ske-keypad", keypad);
+ if (error) {
+ dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
+ goto err_clk_disable;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", error);
+ goto err_free_irq;
+ }
+
+ if (plat->wakeup_enable)
+ device_init_wakeup(&pdev->dev, true);
+
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+err_free_irq:
+ free_irq(keypad->irq, keypad);
+err_clk_disable:
+ clk_disable(keypad->clk);
+ clk_put(keypad->clk);
+err_iounmap:
+ iounmap(keypad->reg_base);
+err_free_mem_region:
+ release_mem_region(res->start, resource_size(res));
+err_free_mem:
+ input_free_device(input);
+ kfree(keypad);
+ return error;
+}
+
+static int __devexit ske_keypad_remove(struct platform_device *pdev)
+{
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ free_irq(keypad->irq, keypad);
+
+ input_unregister_device(keypad->input);
+
+ clk_disable(keypad->clk);
+ clk_put(keypad->clk);
+
+ if (keypad->board->exit)
+ keypad->board->exit();
+
+ iounmap(keypad->reg_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ske_keypad_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(irq);
+ else
+ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+
+ return 0;
+}
+
+static int ske_keypad_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(irq);
+ else
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
+ .suspend = ske_keypad_suspend,
+ .resume = ske_keypad_resume,
+};
+#endif
+
+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),
+};
+
+static int __init ske_keypad_init(void)
+{
+ return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);
+}
+module_init(ske_keypad_init);
+
+static void __exit ske_keypad_exit(void)
+{
+ platform_driver_unregister(&ske_keypad_driver);
+}
+module_exit(ske_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
+MODULE_ALIAS("platform:nomadik-ske-keypad");
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
new file mode 100644
index 000000000000..45bd0977d006
--- /dev/null
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -0,0 +1,318 @@
+/*
+ * OMAP4 Keypad Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author: Abraham Arce <x0066660@ti.com>
+ * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <plat/omap4-keypad.h>
+
+/* OMAP4 registers */
+#define OMAP4_KBD_REVISION 0x00
+#define OMAP4_KBD_SYSCONFIG 0x10
+#define OMAP4_KBD_SYSSTATUS 0x14
+#define OMAP4_KBD_IRQSTATUS 0x18
+#define OMAP4_KBD_IRQENABLE 0x1C
+#define OMAP4_KBD_WAKEUPENABLE 0x20
+#define OMAP4_KBD_PENDING 0x24
+#define OMAP4_KBD_CTRL 0x28
+#define OMAP4_KBD_DEBOUNCINGTIME 0x2C
+#define OMAP4_KBD_LONGKEYTIME 0x30
+#define OMAP4_KBD_TIMEOUT 0x34
+#define OMAP4_KBD_STATEMACHINE 0x38
+#define OMAP4_KBD_ROWINPUTS 0x3C
+#define OMAP4_KBD_COLUMNOUTPUTS 0x40
+#define OMAP4_KBD_FULLCODE31_0 0x44
+#define OMAP4_KBD_FULLCODE63_32 0x48
+
+/* OMAP4 bit definitions */
+#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0)
+#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1)
+#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2)
+#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0)
+#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1)
+#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1)
+#define OMAP4_DEF_CTRLPTVVALUE (1 << 2)
+#define OMAP4_DEF_CTRLPTV (1 << 1)
+
+/* OMAP4 values */
+#define OMAP4_VAL_IRQDISABLE 0x00
+#define OMAP4_VAL_DEBOUNCINGTIME 0x07
+#define OMAP4_VAL_FUNCTIONALCFG 0x1E
+
+#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF
+
+struct omap4_keypad {
+ struct input_dev *input;
+
+ void __iomem *base;
+ int irq;
+
+ unsigned int rows;
+ unsigned int cols;
+ unsigned int row_shift;
+ unsigned char key_state[8];
+ unsigned short keymap[];
+};
+
+static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data)
+{
+ __raw_writel(OMAP4_VAL_FUNCTIONALCFG,
+ keypad_data->base + OMAP4_KBD_CTRL);
+ __raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
+ keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
+ __raw_writel(OMAP4_VAL_IRQDISABLE,
+ keypad_data->base + OMAP4_KBD_IRQSTATUS);
+ __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+ __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
+ keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
+}
+
+/* Interrupt handler */
+static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
+{
+ struct omap4_keypad *keypad_data = dev_id;
+ struct input_dev *input_dev = keypad_data->input;
+ unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
+ unsigned int col, row, code, changed;
+ u32 *new_state = (u32 *) key_state;
+
+ /* Disable interrupts */
+ __raw_writel(OMAP4_VAL_IRQDISABLE,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+
+ *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);
+ *(new_state + 1) = __raw_readl(keypad_data->base
+ + OMAP4_KBD_FULLCODE63_32);
+
+ for (row = 0; row < keypad_data->rows; row++) {
+ changed = key_state[row] ^ keypad_data->key_state[row];
+ if (!changed)
+ continue;
+
+ for (col = 0; col < keypad_data->cols; col++) {
+ if (changed & (1 << col)) {
+ code = MATRIX_SCAN_CODE(row, col,
+ keypad_data->row_shift);
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev,
+ keypad_data->keymap[code],
+ key_state[row] & (1 << col));
+ }
+ }
+ }
+
+ input_sync(input_dev);
+
+ memcpy(keypad_data->key_state, key_state,
+ sizeof(keypad_data->key_state));
+
+ /* clear pending interrupts */
+ __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
+ keypad_data->base + OMAP4_KBD_IRQSTATUS);
+
+ /* enable interrupts */
+ __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit omap4_keypad_probe(struct platform_device *pdev)
+{
+ const struct omap4_keypad_platform_data *pdata;
+ struct omap4_keypad *keypad_data;
+ struct input_dev *input_dev;
+ struct resource *res;
+ resource_size_t size;
+ unsigned int row_shift, max_keys;
+ int irq;
+ int error;
+
+ /* platform data */
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no base address specified\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no keyboard irq assigned\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->keymap_data) {
+ dev_err(&pdev->dev, "no keymap data defined\n");
+ return -EINVAL;
+ }
+
+ row_shift = get_count_order(pdata->cols);
+ max_keys = pdata->rows << row_shift;
+
+ keypad_data = kzalloc(sizeof(struct omap4_keypad) +
+ max_keys * sizeof(keypad_data->keymap[0]),
+ GFP_KERNEL);
+ if (!keypad_data) {
+ dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ size = resource_size(res);
+
+ res = request_mem_region(res->start, size, pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "can't request mem region\n");
+ error = -EBUSY;
+ goto err_free_keypad;
+ }
+
+ keypad_data->base = ioremap(res->start, resource_size(res));
+ if (!keypad_data->base) {
+ dev_err(&pdev->dev, "can't ioremap mem resource\n");
+ error = -ENOMEM;
+ goto err_release_mem;
+ }
+
+ keypad_data->irq = irq;
+ keypad_data->row_shift = row_shift;
+ keypad_data->rows = pdata->rows;
+ keypad_data->cols = pdata->cols;
+
+ /* input device allocation */
+ keypad_data->input = input_dev = input_allocate_device();
+ if (!input_dev) {
+ error = -ENOMEM;
+ goto err_unmap;
+ }
+
+ input_dev->name = pdev->name;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0001;
+
+ input_dev->keycode = keypad_data->keymap;
+ input_dev->keycodesize = sizeof(keypad_data->keymap[0]);
+ input_dev->keycodemax = max_keys;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_REP, input_dev->evbit);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+ input_set_drvdata(input_dev, keypad_data);
+
+ matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
+ input_dev->keycode, input_dev->keybit);
+
+ omap4_keypad_config(keypad_data);
+
+ error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
+ IRQF_TRIGGER_RISING,
+ "omap4-keypad", keypad_data);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register interrupt\n");
+ goto err_free_input;
+ }
+
+ error = input_register_device(keypad_data->input);
+ if (error < 0) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto err_free_irq;
+ }
+
+
+ platform_set_drvdata(pdev, keypad_data);
+ return 0;
+
+err_free_irq:
+ free_irq(keypad_data->irq, keypad_data);
+err_free_input:
+ input_free_device(input_dev);
+err_unmap:
+ iounmap(keypad_data->base);
+err_release_mem:
+ release_mem_region(res->start, size);
+err_free_keypad:
+ kfree(keypad_data);
+ return error;
+}
+
+static int __devexit omap4_keypad_remove(struct platform_device *pdev)
+{
+ struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(keypad_data->irq, keypad_data);
+ input_unregister_device(keypad_data->input);
+
+ iounmap(keypad_data->base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(keypad_data);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver omap4_keypad_driver = {
+ .probe = omap4_keypad_probe,
+ .remove = __devexit_p(omap4_keypad_remove),
+ .driver = {
+ .name = "omap4-keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap4_keypad_init(void)
+{
+ return platform_driver_register(&omap4_keypad_driver);
+}
+module_init(omap4_keypad_init);
+
+static void __exit omap4_keypad_exit(void)
+{
+ platform_driver_unregister(&omap4_keypad_driver);
+}
+module_exit(omap4_keypad_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP4 Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap4-keypad");
diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c
new file mode 100644
index 000000000000..b4a81ebfab92
--- /dev/null
+++ b/drivers/input/keyboard/tnetv107x-keypad.c
@@ -0,0 +1,340 @@
+/*
+ * Texas Instruments TNETV107X Keypad Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation 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/errno.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/input/matrix_keypad.h>
+
+#define BITS(x) (BIT(x) - 1)
+
+#define KEYPAD_ROWS 9
+#define KEYPAD_COLS 9
+
+#define DEBOUNCE_MIN 0x400ul
+#define DEBOUNCE_MAX 0x3ffffffful
+
+struct keypad_regs {
+ u32 rev;
+ u32 mode;
+ u32 mask;
+ u32 pol;
+ u32 dclock;
+ u32 rclock;
+ u32 stable_cnt;
+ u32 in_en;
+ u32 out;
+ u32 out_en;
+ u32 in;
+ u32 lock;
+ u32 pres[3];
+};
+
+#define keypad_read(kp, reg) __raw_readl(&(kp)->regs->reg)
+#define keypad_write(kp, reg, val) __raw_writel(val, &(kp)->regs->reg)
+
+struct keypad_data {
+ struct input_dev *input_dev;
+ struct resource *res;
+ struct keypad_regs __iomem *regs;
+ struct clk *clk;
+ struct device *dev;
+ spinlock_t lock;
+ u32 irq_press;
+ u32 irq_release;
+ int rows, cols, row_shift;
+ int debounce_ms, active_low;
+ u32 prev_keys[3];
+ unsigned short keycodes[];
+};
+
+static irqreturn_t keypad_irq(int irq, void *data)
+{
+ struct keypad_data *kp = data;
+ int i, bit, val, row, col, code;
+ unsigned long flags;
+ u32 curr_keys[3];
+ u32 change;
+
+ spin_lock_irqsave(&kp->lock, flags);
+
+ memset(curr_keys, 0, sizeof(curr_keys));
+ if (irq == kp->irq_press)
+ for (i = 0; i < 3; i++)
+ curr_keys[i] = keypad_read(kp, pres[i]);
+
+ for (i = 0; i < 3; i++) {
+ change = curr_keys[i] ^ kp->prev_keys[i];
+
+ while (change) {
+ bit = fls(change) - 1;
+ change ^= BIT(bit);
+ val = curr_keys[i] & BIT(bit);
+ bit += i * 32;
+ row = bit / KEYPAD_COLS;
+ col = bit % KEYPAD_COLS;
+
+ code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
+ input_event(kp->input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(kp->input_dev, kp->keycodes[code],
+ val);
+ }
+ }
+ input_sync(kp->input_dev);
+ memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys));
+
+ if (irq == kp->irq_press)
+ keypad_write(kp, lock, 0); /* Allow hardware updates */
+
+ spin_unlock_irqrestore(&kp->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int keypad_start(struct input_dev *dev)
+{
+ struct keypad_data *kp = input_get_drvdata(dev);
+ unsigned long mask, debounce, clk_rate_khz;
+ unsigned long flags;
+
+ clk_enable(kp->clk);
+ clk_rate_khz = clk_get_rate(kp->clk) / 1000;
+
+ spin_lock_irqsave(&kp->lock, flags);
+
+ /* Initialize device registers */
+ keypad_write(kp, mode, 0);
+
+ mask = BITS(kp->rows) << KEYPAD_COLS;
+ mask |= BITS(kp->cols);
+ keypad_write(kp, mask, ~mask);
+
+ keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff);
+ keypad_write(kp, stable_cnt, 3);
+
+ debounce = kp->debounce_ms * clk_rate_khz;
+ debounce = clamp(debounce, DEBOUNCE_MIN, DEBOUNCE_MAX);
+ keypad_write(kp, dclock, debounce);
+ keypad_write(kp, rclock, 4 * debounce);
+
+ keypad_write(kp, in_en, 1);
+
+ spin_unlock_irqrestore(&kp->lock, flags);
+
+ return 0;
+}
+
+static void keypad_stop(struct input_dev *dev)
+{
+ struct keypad_data *kp = input_get_drvdata(dev);
+
+ synchronize_irq(kp->irq_press);
+ synchronize_irq(kp->irq_release);
+ clk_disable(kp->clk);
+}
+
+static int __devinit keypad_probe(struct platform_device *pdev)
+{
+ const struct matrix_keypad_platform_data *pdata;
+ const struct matrix_keymap_data *keymap_data;
+ struct device *dev = &pdev->dev;
+ struct keypad_data *kp;
+ int error = 0, sz, row_shift;
+ u32 rev = 0;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(dev, "cannot find device data\n");
+ return -EINVAL;
+ }
+
+ keymap_data = pdata->keymap_data;
+ if (!keymap_data) {
+ dev_err(dev, "cannot find keymap data\n");
+ return -EINVAL;
+ }
+
+ row_shift = get_count_order(pdata->num_col_gpios);
+ sz = offsetof(struct keypad_data, keycodes);
+ sz += (pdata->num_row_gpios << row_shift) * sizeof(kp->keycodes[0]);
+ kp = kzalloc(sz, GFP_KERNEL);
+ if (!kp) {
+ dev_err(dev, "cannot allocate device info\n");
+ return -ENOMEM;
+ }
+
+ kp->dev = dev;
+ kp->rows = pdata->num_row_gpios;
+ kp->cols = pdata->num_col_gpios;
+ kp->row_shift = row_shift;
+ platform_set_drvdata(pdev, kp);
+ spin_lock_init(&kp->lock);
+
+ kp->irq_press = platform_get_irq_byname(pdev, "press");
+ kp->irq_release = platform_get_irq_byname(pdev, "release");
+ if (kp->irq_press < 0 || kp->irq_release < 0) {
+ dev_err(dev, "cannot determine device interrupts\n");
+ error = -ENODEV;
+ goto error_res;
+ }
+
+ kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!kp->res) {
+ dev_err(dev, "cannot determine register area\n");
+ error = -ENODEV;
+ goto error_res;
+ }
+
+ if (!request_mem_region(kp->res->start, resource_size(kp->res),
+ pdev->name)) {
+ dev_err(dev, "cannot claim register memory\n");
+ kp->res = NULL;
+ error = -EINVAL;
+ goto error_res;
+ }
+
+ kp->regs = ioremap(kp->res->start, resource_size(kp->res));
+ if (!kp->regs) {
+ dev_err(dev, "cannot map register memory\n");
+ error = -ENOMEM;
+ goto error_map;
+ }
+
+ kp->clk = clk_get(dev, NULL);
+ if (!kp->clk) {
+ dev_err(dev, "cannot claim device clock\n");
+ error = -EINVAL;
+ goto error_clk;
+ }
+
+ error = request_threaded_irq(kp->irq_press, NULL, keypad_irq, 0,
+ dev_name(dev), kp);
+ if (error < 0) {
+ dev_err(kp->dev, "Could not allocate keypad press key irq\n");
+ goto error_irq_press;
+ }
+
+ error = request_threaded_irq(kp->irq_release, NULL, keypad_irq, 0,
+ dev_name(dev), kp);
+ if (error < 0) {
+ dev_err(kp->dev, "Could not allocate keypad release key irq\n");
+ goto error_irq_release;
+ }
+
+ kp->input_dev = input_allocate_device();
+ if (!kp->input_dev) {
+ dev_err(dev, "cannot allocate input device\n");
+ error = -ENOMEM;
+ goto error_input;
+ }
+ input_set_drvdata(kp->input_dev, kp);
+
+ kp->input_dev->name = pdev->name;
+ kp->input_dev->dev.parent = &pdev->dev;
+ kp->input_dev->open = keypad_start;
+ kp->input_dev->close = keypad_stop;
+ kp->input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ if (!pdata->no_autorepeat)
+ kp->input_dev->evbit[0] |= BIT_MASK(EV_REP);
+
+ clk_enable(kp->clk);
+ rev = keypad_read(kp, rev);
+ kp->input_dev->id.bustype = BUS_HOST;
+ kp->input_dev->id.product = ((rev >> 8) & 0x07);
+ kp->input_dev->id.version = ((rev >> 16) & 0xfff);
+ clk_disable(kp->clk);
+
+ kp->input_dev->keycode = kp->keycodes;
+ kp->input_dev->keycodesize = sizeof(kp->keycodes[0]);
+ kp->input_dev->keycodemax = kp->rows << kp->row_shift;
+
+ matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes,
+ kp->input_dev->keybit);
+
+ input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN);
+
+ error = input_register_device(kp->input_dev);
+ if (error < 0) {
+ dev_err(dev, "Could not register input device\n");
+ goto error_reg;
+ }
+
+ return 0;
+
+
+error_reg:
+ input_free_device(kp->input_dev);
+error_input:
+ free_irq(kp->irq_release, kp);
+error_irq_release:
+ free_irq(kp->irq_press, kp);
+error_irq_press:
+ clk_put(kp->clk);
+error_clk:
+ iounmap(kp->regs);
+error_map:
+ release_mem_region(kp->res->start, resource_size(kp->res));
+error_res:
+ platform_set_drvdata(pdev, NULL);
+ kfree(kp);
+ return error;
+}
+
+static int __devexit keypad_remove(struct platform_device *pdev)
+{
+ struct keypad_data *kp = platform_get_drvdata(pdev);
+
+ free_irq(kp->irq_press, kp);
+ free_irq(kp->irq_release, kp);
+ input_unregister_device(kp->input_dev);
+ clk_put(kp->clk);
+ iounmap(kp->regs);
+ release_mem_region(kp->res->start, resource_size(kp->res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(kp);
+
+ return 0;
+}
+
+static struct platform_driver keypad_driver = {
+ .probe = keypad_probe,
+ .remove = __devexit_p(keypad_remove),
+ .driver.name = "tnetv107x-keypad",
+ .driver.owner = THIS_MODULE,
+};
+
+static int __init keypad_init(void)
+{
+ return platform_driver_register(&keypad_driver);
+}
+
+static void __exit keypad_exit(void)
+{
+ platform_driver_unregister(&keypad_driver);
+}
+
+module_init(keypad_init);
+module_exit(keypad_exit);
+
+MODULE_AUTHOR("Cyril Chemparathy");
+MODULE_DESCRIPTION("TNETV107X Keypad Driver");
+MODULE_ALIAS("platform: tnetv107x-keypad");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
index fb16b5e5ea13..09bef79d9da1 100644
--- a/drivers/input/keyboard/twl4030_keypad.c
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -406,23 +406,22 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
if (error) {
dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
kp->irq);
- goto err3;
+ goto err2;
}
/* Enable KP and TO interrupts now. */
reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
error = -EIO;
- goto err4;
+ goto err3;
}
platform_set_drvdata(pdev, kp);
return 0;
-err4:
+err3:
/* mask all events - we don't care about the result */
(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
-err3:
free_irq(kp->irq, NULL);
err2:
input_unregister_device(input);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index b49e23379723..b99b8cbde02f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY
To compile this driver as a module, choose M here: the module
will be called 88pm860x_onkey.
+config INPUT_AB8500_PONKEY
+ tristate "AB8500 Pon (PowerOn) Key"
+ depends on AB8500_CORE
+ help
+ Say Y here to use the PowerOn Key for ST-Ericsson's AB8500
+ Mix-Sig PMIC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ab8500-ponkey.
+
config INPUT_AD714X
tristate "Analog Devices AD714x Capacitance Touch Sensor"
help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 19ccca78fa76..1fe1f6c8b737 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
+obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
new file mode 100644
index 000000000000..3d3288a78fdc
--- /dev/null
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 Power-On Key handler
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/slab.h>
+
+/**
+ * struct ab8500_ponkey - ab8500 ponkey information
+ * @input_dev: pointer to input device
+ * @ab8500: ab8500 parent
+ * @irq_dbf: irq number for falling transition
+ * @irq_dbr: irq number for rising transition
+ */
+struct ab8500_ponkey {
+ struct input_dev *idev;
+ struct ab8500 *ab8500;
+ int irq_dbf;
+ int irq_dbr;
+};
+
+/* AB8500 gives us an interrupt when ONKEY is held */
+static irqreturn_t ab8500_ponkey_handler(int irq, void *data)
+{
+ struct ab8500_ponkey *ponkey = data;
+
+ if (irq == ponkey->irq_dbf)
+ input_report_key(ponkey->idev, KEY_POWER, true);
+ else if (irq == ponkey->irq_dbr)
+ input_report_key(ponkey->idev, KEY_POWER, false);
+
+ input_sync(ponkey->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_ponkey *ponkey;
+ struct input_dev *input;
+ int irq_dbf, irq_dbr;
+ int error;
+
+ irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF");
+ if (irq_dbf < 0) {
+ dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf);
+ return irq_dbf;
+ }
+
+ irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR");
+ if (irq_dbr < 0) {
+ dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr);
+ return irq_dbr;
+ }
+
+ ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!ponkey || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ponkey->idev = input;
+ ponkey->ab8500 = ab8500;
+ ponkey->irq_dbf = irq_dbf;
+ ponkey->irq_dbr = irq_dbr;
+
+ input->name = "AB8500 POn(PowerOn) Key";
+ input->dev.parent = &pdev->dev;
+
+ input_set_capability(input, EV_KEY, KEY_POWER);
+
+ error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler,
+ 0, "ab8500-ponkey-dbf", ponkey);
+ if (error < 0) {
+ dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n",
+ ponkey->irq_dbf, error);
+ goto err_free_mem;
+ }
+
+ error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler,
+ 0, "ab8500-ponkey-dbr", ponkey);
+ if (error < 0) {
+ dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n",
+ ponkey->irq_dbr, error);
+ goto err_free_dbf_irq;
+ }
+
+ error = input_register_device(ponkey->idev);
+ if (error) {
+ dev_err(ab8500->dev, "Can't register input device: %d\n", error);
+ goto err_free_dbr_irq;
+ }
+
+ platform_set_drvdata(pdev, ponkey);
+ return 0;
+
+err_free_dbr_irq:
+ free_irq(ponkey->irq_dbr, ponkey);
+err_free_dbf_irq:
+ free_irq(ponkey->irq_dbf, ponkey);
+err_free_mem:
+ input_free_device(input);
+ kfree(ponkey);
+
+ return error;
+}
+
+static int __devexit ab8500_ponkey_remove(struct platform_device *pdev)
+{
+ struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev);
+
+ free_irq(ponkey->irq_dbf, ponkey);
+ free_irq(ponkey->irq_dbr, ponkey);
+ input_unregister_device(ponkey->idev);
+ kfree(ponkey);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_ponkey_driver = {
+ .driver = {
+ .name = "ab8500-poweron-key",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_ponkey_probe,
+ .remove = __devexit_p(ab8500_ponkey_remove),
+};
+
+static int __init ab8500_ponkey_init(void)
+{
+ return platform_driver_register(&ab8500_ponkey_driver);
+}
+module_init(ab8500_ponkey_init);
+
+static void __exit ab8500_ponkey_exit(void)
+{
+ platform_driver_unregister(&ab8500_ponkey_driver);
+}
+module_exit(ab8500_ponkey_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver");
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index 23257652b8e8..0b0e9be63542 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -483,51 +483,88 @@ static void ati_remote2_complete_key(struct urb *urb)
}
static int ati_remote2_getkeycode(struct input_dev *idev,
- unsigned int scancode, unsigned int *keycode)
+ struct input_keymap_entry *ke)
{
struct ati_remote2 *ar2 = input_get_drvdata(idev);
unsigned int mode;
- int index;
+ int offset;
+ unsigned int index;
+ unsigned int scancode;
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+ index = ke->index;
+ if (index >= ATI_REMOTE2_MODES *
+ ARRAY_SIZE(ati_remote2_key_table))
+ return -EINVAL;
+
+ mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+ offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+ scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code;
+ } else {
+ if (input_scancode_to_scalar(ke, &scancode))
+ return -EINVAL;
+
+ mode = scancode >> 8;
+ if (mode > ATI_REMOTE2_PC)
+ return -EINVAL;
+
+ offset = ati_remote2_lookup(scancode & 0xff);
+ if (offset < 0)
+ return -EINVAL;
+
+ index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset;
+ }
- mode = scancode >> 8;
- if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
- return -EINVAL;
+ ke->keycode = ar2->keycode[mode][offset];
+ ke->len = sizeof(scancode);
+ memcpy(&ke->scancode, &scancode, sizeof(scancode));
+ ke->index = index;
- index = ati_remote2_lookup(scancode & 0xFF);
- if (index < 0)
- return -EINVAL;
-
- *keycode = ar2->keycode[mode][index];
return 0;
}
static int ati_remote2_setkeycode(struct input_dev *idev,
- unsigned int scancode, unsigned int keycode)
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
{
struct ati_remote2 *ar2 = input_get_drvdata(idev);
- unsigned int mode, old_keycode;
- int index;
-
- mode = scancode >> 8;
- if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
- return -EINVAL;
-
- index = ati_remote2_lookup(scancode & 0xFF);
- if (index < 0)
- return -EINVAL;
+ unsigned int mode;
+ int offset;
+ unsigned int index;
+ unsigned int scancode;
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+ if (ke->index >= ATI_REMOTE2_MODES *
+ ARRAY_SIZE(ati_remote2_key_table))
+ return -EINVAL;
+
+ mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+ offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+ } else {
+ if (input_scancode_to_scalar(ke, &scancode))
+ return -EINVAL;
+
+ mode = scancode >> 8;
+ if (mode > ATI_REMOTE2_PC)
+ return -EINVAL;
+
+ offset = ati_remote2_lookup(scancode & 0xff);
+ if (offset < 0)
+ return -EINVAL;
+ }
- old_keycode = ar2->keycode[mode][index];
- ar2->keycode[mode][index] = keycode;
- __set_bit(keycode, idev->keybit);
+ *old_keycode = ar2->keycode[mode][offset];
+ ar2->keycode[mode][offset] = ke->keycode;
+ __set_bit(ke->keycode, idev->keybit);
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
- if (ar2->keycode[mode][index] == old_keycode)
+ if (ar2->keycode[mode][index] == *old_keycode)
return 0;
}
}
- __clear_bit(old_keycode, idev->keybit);
+ __clear_bit(*old_keycode, idev->keybit);
return 0;
}
@@ -575,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
idev->open = ati_remote2_open;
idev->close = ati_remote2_close;
- idev->getkeycode = ati_remote2_getkeycode;
- idev->setkeycode = ati_remote2_setkeycode;
+ idev->getkeycode_new = ati_remote2_getkeycode;
+ idev->setkeycode_new = ati_remote2_setkeycode;
idev->name = ar2->name;
idev->phys = ar2->phys;
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
index bf170f6b4422..f45947190e4f 100644
--- a/drivers/input/misc/powermate.c
+++ b/drivers/input/misc/powermate.c
@@ -280,7 +280,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev
pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL);
if (!pm->configcr)
- return -1;
+ return -ENOMEM;
return 0;
}
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index 4f9b2afc24e8..014dd4ad0d4f 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -271,7 +271,7 @@ static struct platform_driver twl4030_vibra_driver = {
.probe = twl4030_vibra_probe,
.remove = __devexit_p(twl4030_vibra_remove),
.driver = {
- .name = "twl4030_codec_vibra",
+ .name = "twl4030-vibra",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &twl4030_vibra_pm_ops,
@@ -291,7 +291,7 @@ static void __exit twl4030_vibra_exit(void)
}
module_exit(twl4030_vibra_exit);
-MODULE_ALIAS("platform:twl4030_codec_vibra");
+MODULE_ALIAS("platform:twl4030-vibra");
MODULE_DESCRIPTION("TWL4030 Vibra driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 48311204ba51..04d9bf320a4f 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -699,7 +699,7 @@ int elantech_init(struct psmouse *psmouse)
psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
if (!etd)
- return -1;
+ return -ENOMEM;
etd->parity[0] = 1;
for (i = 1; i < 256; i++)
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 73a7af2542a8..cd9d0c97e429 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
if (!new_dev)
return -ENOMEM;
- while (serio->child) {
+ while (!list_empty(&serio->children)) {
if (++retry > 3) {
printk(KERN_WARNING
- "psmouse: failed to destroy child port, "
+ "psmouse: failed to destroy children ports, "
"protocol change aborted.\n");
input_free_device(new_dev);
return -EIO;
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 96b70a43515f..2e300a460556 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -294,7 +294,29 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c)
return 0;
}
-static inline int synaptics_is_pt_packet(unsigned char *buf)
+static int synaptics_pt_start(struct serio *serio)
+{
+ struct psmouse *parent = serio_get_drvdata(serio->parent);
+ struct synaptics_data *priv = parent->private;
+
+ serio_pause_rx(parent->ps2dev.serio);
+ priv->pt_port = serio;
+ serio_continue_rx(parent->ps2dev.serio);
+
+ return 0;
+}
+
+static void synaptics_pt_stop(struct serio *serio)
+{
+ struct psmouse *parent = serio_get_drvdata(serio->parent);
+ struct synaptics_data *priv = parent->private;
+
+ serio_pause_rx(parent->ps2dev.serio);
+ priv->pt_port = NULL;
+ serio_continue_rx(parent->ps2dev.serio);
+}
+
+static int synaptics_is_pt_packet(unsigned char *buf)
{
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
}
@@ -315,9 +337,8 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
static void synaptics_pt_activate(struct psmouse *psmouse)
{
- struct serio *ptport = psmouse->ps2dev.serio->child;
- struct psmouse *child = serio_get_drvdata(ptport);
struct synaptics_data *priv = psmouse->private;
+ struct psmouse *child = serio_get_drvdata(priv->pt_port);
/* adjust the touchpad to child's choice of protocol */
if (child) {
@@ -345,6 +366,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
serio->write = synaptics_pt_write;
+ serio->start = synaptics_pt_start;
+ serio->stop = synaptics_pt_stop;
serio->parent = psmouse->ps2dev.serio;
psmouse->pt_activate = synaptics_pt_activate;
@@ -578,9 +601,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
- if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
- if (psmouse->ps2dev.serio->child)
- synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
+ if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
+ synaptics_is_pt_packet(psmouse->packet)) {
+ if (priv->pt_port)
+ synaptics_pass_pt_packet(priv->pt_port, psmouse->packet);
} else
synaptics_process_packet(psmouse);
@@ -731,7 +755,7 @@ int synaptics_init(struct psmouse *psmouse)
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
if (!priv)
- return -1;
+ return -ENOMEM;
psmouse_reset(psmouse);
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index b6aa7d20d8a3..613a3652f98f 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -110,6 +110,8 @@ struct synaptics_data {
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */
int scroll;
+
+ struct serio *pt_port; /* Pass-through serio port */
};
void synaptics_module_init(void);
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index 0643e49ca603..54b2fa892e19 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -303,7 +303,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
if (!psmouse->private)
- return -1;
+ return -ENOMEM;
psmouse->vendor = "IBM";
psmouse->name = "TrackPoint";
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index 31ec7265aac6..2a00ddf4f23a 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -867,7 +867,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
spin_lock_init(&mousedev->client_lock);
mutex_init(&mousedev->mutex);
lockdep_set_subclass(&mousedev->mutex,
- minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
+ minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0);
init_waitqueue_head(&mousedev->wait);
if (minor == MOUSEDEV_MIX)
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 3bfe8fafc6ad..6256233d2bfb 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -226,4 +226,13 @@ config SERIO_AMS_DELTA
To compile this driver as a module, choose M here;
the module will be called ams_delta_serio.
+config SERIO_PS2MULT
+ tristate "TQC PS/2 multiplexer"
+ help
+ Say Y here if you have the PS/2 line multiplexer like the one
+ present on TQC boads.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ps2mult.
+
endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 84c80bf7185e..dbbe37616c92 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
obj-$(CONFIG_HP_SDC) += hp_sdc.o
obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
+obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index f58513160480..18db5a8c7478 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state)
#ifdef CONFIG_X86
static void i8042_dritek_enable(void)
{
- char param = 0x90;
+ unsigned char param = 0x90;
int error;
error = i8042_command(&param, 0x1059);
diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c
new file mode 100644
index 000000000000..6bce22e4e495
--- /dev/null
+++ b/drivers/input/serio/ps2mult.c
@@ -0,0 +1,318 @@
+/*
+ * TQC PS/2 Multiplexer driver
+ *
+ * Copyright (C) 2010 Dmitry Eremin-Solenikov
+ *
+ * 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/slab.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
+MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
+MODULE_LICENSE("GPL");
+
+#define PS2MULT_KB_SELECTOR 0xA0
+#define PS2MULT_MS_SELECTOR 0xA1
+#define PS2MULT_ESCAPE 0x7D
+#define PS2MULT_BSYNC 0x7E
+#define PS2MULT_SESSION_START 0x55
+#define PS2MULT_SESSION_END 0x56
+
+struct ps2mult_port {
+ struct serio *serio;
+ unsigned char sel;
+ bool registered;
+};
+
+#define PS2MULT_NUM_PORTS 2
+#define PS2MULT_KBD_PORT 0
+#define PS2MULT_MOUSE_PORT 1
+
+struct ps2mult {
+ struct serio *mx_serio;
+ struct ps2mult_port ports[PS2MULT_NUM_PORTS];
+
+ spinlock_t lock;
+ struct ps2mult_port *in_port;
+ struct ps2mult_port *out_port;
+ bool escape;
+};
+
+/* First MUST come PS2MULT_NUM_PORTS selectors */
+static const unsigned char ps2mult_controls[] = {
+ PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
+ PS2MULT_ESCAPE, PS2MULT_BSYNC,
+ PS2MULT_SESSION_START, PS2MULT_SESSION_END,
+};
+
+static const struct serio_device_id ps2mult_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PS2MULT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
+
+static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
+{
+ struct serio *mx_serio = psm->mx_serio;
+
+ serio_write(mx_serio, port->sel);
+ psm->out_port = port;
+ dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
+}
+
+static int ps2mult_serio_write(struct serio *serio, unsigned char data)
+{
+ struct serio *mx_port = serio->parent;
+ struct ps2mult *psm = serio_get_drvdata(mx_port);
+ struct ps2mult_port *port = serio->port_data;
+ bool need_escape;
+ unsigned long flags;
+
+ spin_lock_irqsave(&psm->lock, flags);
+
+ if (psm->out_port != port)
+ ps2mult_select_port(psm, port);
+
+ need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
+
+ dev_dbg(&serio->dev,
+ "write: %s%02x\n", need_escape ? "ESC " : "", data);
+
+ if (need_escape)
+ serio_write(mx_port, PS2MULT_ESCAPE);
+
+ serio_write(mx_port, data);
+
+ spin_unlock_irqrestore(&psm->lock, flags);
+
+ return 0;
+}
+
+static int ps2mult_serio_start(struct serio *serio)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio->parent);
+ struct ps2mult_port *port = serio->port_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&psm->lock, flags);
+ port->registered = true;
+ spin_unlock_irqrestore(&psm->lock, flags);
+
+ return 0;
+}
+
+static void ps2mult_serio_stop(struct serio *serio)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio->parent);
+ struct ps2mult_port *port = serio->port_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&psm->lock, flags);
+ port->registered = false;
+ spin_unlock_irqrestore(&psm->lock, flags);
+}
+
+static int ps2mult_create_port(struct ps2mult *psm, int i)
+{
+ struct serio *mx_serio = psm->mx_serio;
+ struct serio *serio;
+
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
+ snprintf(serio->phys, sizeof(serio->phys),
+ "%s/port%d", mx_serio->phys, i);
+ serio->id.type = SERIO_8042;
+ serio->write = ps2mult_serio_write;
+ serio->start = ps2mult_serio_start;
+ serio->stop = ps2mult_serio_stop;
+ serio->parent = psm->mx_serio;
+ serio->port_data = &psm->ports[i];
+
+ psm->ports[i].serio = serio;
+
+ return 0;
+}
+
+static void ps2mult_reset(struct ps2mult *psm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&psm->lock, flags);
+
+ serio_write(psm->mx_serio, PS2MULT_SESSION_END);
+ serio_write(psm->mx_serio, PS2MULT_SESSION_START);
+
+ ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
+
+ spin_unlock_irqrestore(&psm->lock, flags);
+}
+
+static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct ps2mult *psm;
+ int i;
+ int error;
+
+ if (!serio->write)
+ return -EINVAL;
+
+ psm = kzalloc(sizeof(*psm), GFP_KERNEL);
+ if (!psm)
+ return -ENOMEM;
+
+ spin_lock_init(&psm->lock);
+ psm->mx_serio = serio;
+
+ for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
+ psm->ports[i].sel = ps2mult_controls[i];
+ error = ps2mult_create_port(psm, i);
+ if (error)
+ goto err_out;
+ }
+
+ psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
+
+ serio_set_drvdata(serio, psm);
+ error = serio_open(serio, drv);
+ if (error)
+ goto err_out;
+
+ ps2mult_reset(psm);
+
+ for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
+ struct serio *s = psm->ports[i].serio;
+
+ dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
+ serio_register_port(s);
+ }
+
+ return 0;
+
+err_out:
+ while (--i >= 0)
+ kfree(psm->ports[i].serio);
+ kfree(serio);
+ return error;
+}
+
+static void ps2mult_disconnect(struct serio *serio)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio);
+
+ /* Note that serio core already take care of children ports */
+ serio_write(serio, PS2MULT_SESSION_END);
+ serio_close(serio);
+ kfree(psm);
+
+ serio_set_drvdata(serio, NULL);
+}
+
+static int ps2mult_reconnect(struct serio *serio)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio);
+
+ ps2mult_reset(psm);
+
+ return 0;
+}
+
+static irqreturn_t ps2mult_interrupt(struct serio *serio,
+ unsigned char data, unsigned int dfl)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio);
+ struct ps2mult_port *in_port;
+ unsigned long flags;
+
+ dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
+
+ spin_lock_irqsave(&psm->lock, flags);
+
+ if (psm->escape) {
+ psm->escape = false;
+ in_port = psm->in_port;
+ if (in_port->registered)
+ serio_interrupt(in_port->serio, data, dfl);
+ goto out;
+ }
+
+ switch (data) {
+ case PS2MULT_ESCAPE:
+ dev_dbg(&serio->dev, "ESCAPE\n");
+ psm->escape = true;
+ break;
+
+ case PS2MULT_BSYNC:
+ dev_dbg(&serio->dev, "BSYNC\n");
+ psm->in_port = psm->out_port;
+ break;
+
+ case PS2MULT_SESSION_START:
+ dev_dbg(&serio->dev, "SS\n");
+ break;
+
+ case PS2MULT_SESSION_END:
+ dev_dbg(&serio->dev, "SE\n");
+ break;
+
+ case PS2MULT_KB_SELECTOR:
+ dev_dbg(&serio->dev, "KB\n");
+ psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
+ break;
+
+ case PS2MULT_MS_SELECTOR:
+ dev_dbg(&serio->dev, "MS\n");
+ psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
+ break;
+
+ default:
+ in_port = psm->in_port;
+ if (in_port->registered)
+ serio_interrupt(in_port->serio, data, dfl);
+ break;
+ }
+
+ out:
+ spin_unlock_irqrestore(&psm->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct serio_driver ps2mult_drv = {
+ .driver = {
+ .name = "ps2mult",
+ },
+ .description = "TQC PS/2 Multiplexer driver",
+ .id_table = ps2mult_serio_ids,
+ .interrupt = ps2mult_interrupt,
+ .connect = ps2mult_connect,
+ .disconnect = ps2mult_disconnect,
+ .reconnect = ps2mult_reconnect,
+};
+
+static int __init ps2mult_init(void)
+{
+ return serio_register_driver(&ps2mult_drv);
+}
+
+static void __exit ps2mult_exit(void)
+{
+ serio_unregister_driver(&ps2mult_drv);
+}
+
+module_init(ps2mult_init);
+module_exit(ps2mult_exit);
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index c3b626e9eae7..405bf214527c 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -37,7 +37,6 @@
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
-#include <linux/freezer.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Serio abstraction core");
@@ -56,7 +55,7 @@ static struct bus_type serio_bus;
static void serio_add_port(struct serio *serio);
static int serio_reconnect_port(struct serio *serio);
static void serio_disconnect_port(struct serio *serio);
-static void serio_reconnect_chain(struct serio *serio);
+static void serio_reconnect_subtree(struct serio *serio);
static void serio_attach_driver(struct serio_driver *drv);
static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
@@ -152,7 +151,7 @@ static void serio_find_driver(struct serio *serio)
enum serio_event_type {
SERIO_RESCAN_PORT,
SERIO_RECONNECT_PORT,
- SERIO_RECONNECT_CHAIN,
+ SERIO_RECONNECT_SUBTREE,
SERIO_REGISTER_PORT,
SERIO_ATTACH_DRIVER,
};
@@ -292,8 +291,8 @@ static void serio_handle_event(void)
serio_find_driver(event->object);
break;
- case SERIO_RECONNECT_CHAIN:
- serio_reconnect_chain(event->object);
+ case SERIO_RECONNECT_SUBTREE:
+ serio_reconnect_subtree(event->object);
break;
case SERIO_ATTACH_DRIVER:
@@ -330,12 +329,10 @@ static void serio_remove_pending_events(void *object)
}
/*
- * Destroy child serio port (if any) that has not been fully registered yet.
+ * Locate child serio port (if any) that has not been fully registered yet.
*
- * Note that we rely on the fact that port can have only one child and therefore
- * only one child registration request can be pending. Additionally, children
- * are registered by driver's connect() handler so there can't be a grandchild
- * pending registration together with a child.
+ * Children are registered by driver's connect() handler so there can't be a
+ * grandchild pending registration together with a child.
*/
static struct serio *serio_get_pending_child(struct serio *parent)
{
@@ -449,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
if (!strncmp(buf, "none", count)) {
serio_disconnect_port(serio);
} else if (!strncmp(buf, "reconnect", count)) {
- serio_reconnect_chain(serio);
+ serio_reconnect_subtree(serio);
} else if (!strncmp(buf, "rescan", count)) {
serio_disconnect_port(serio);
serio_find_driver(serio);
@@ -516,6 +513,8 @@ static void serio_init_port(struct serio *serio)
__module_get(THIS_MODULE);
INIT_LIST_HEAD(&serio->node);
+ INIT_LIST_HEAD(&serio->child_node);
+ INIT_LIST_HEAD(&serio->children);
spin_lock_init(&serio->lock);
mutex_init(&serio->drv_mutex);
device_initialize(&serio->dev);
@@ -538,12 +537,13 @@ static void serio_init_port(struct serio *serio)
*/
static void serio_add_port(struct serio *serio)
{
+ struct serio *parent = serio->parent;
int error;
- if (serio->parent) {
- serio_pause_rx(serio->parent);
- serio->parent->child = serio;
- serio_continue_rx(serio->parent);
+ if (parent) {
+ serio_pause_rx(parent);
+ list_add_tail(&serio->child_node, &parent->children);
+ serio_continue_rx(parent);
}
list_add_tail(&serio->node, &serio_list);
@@ -559,15 +559,14 @@ static void serio_add_port(struct serio *serio)
}
/*
- * serio_destroy_port() completes deregistration process and removes
+ * serio_destroy_port() completes unregistration process and removes
* port from the system
*/
static void serio_destroy_port(struct serio *serio)
{
struct serio *child;
- child = serio_get_pending_child(serio);
- if (child) {
+ while ((child = serio_get_pending_child(serio)) != NULL) {
serio_remove_pending_events(child);
put_device(&child->dev);
}
@@ -577,7 +576,7 @@ static void serio_destroy_port(struct serio *serio)
if (serio->parent) {
serio_pause_rx(serio->parent);
- serio->parent->child = NULL;
+ list_del_init(&serio->child_node);
serio_continue_rx(serio->parent);
serio->parent = NULL;
}
@@ -609,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio)
}
/*
- * Reconnect serio port and all its children (re-initialize attached devices)
+ * Reconnect serio port and all its children (re-initialize attached
+ * devices).
*/
-static void serio_reconnect_chain(struct serio *serio)
+static void serio_reconnect_subtree(struct serio *root)
{
+ struct serio *s = root;
+ int error;
+
do {
- if (serio_reconnect_port(serio)) {
- /* Ok, old children are now gone, we are done */
- break;
+ error = serio_reconnect_port(s);
+ if (!error) {
+ /*
+ * Reconnect was successful, move on to do the
+ * first child.
+ */
+ if (!list_empty(&s->children)) {
+ s = list_first_entry(&s->children,
+ struct serio, child_node);
+ continue;
+ }
}
- serio = serio->child;
- } while (serio);
+
+ /*
+ * Either it was a leaf node or reconnect failed and it
+ * became a leaf node. Continue reconnecting starting with
+ * the next sibling of the parent node.
+ */
+ while (s != root) {
+ struct serio *parent = s->parent;
+
+ if (!list_is_last(&s->child_node, &parent->children)) {
+ s = list_entry(s->child_node.next,
+ struct serio, child_node);
+ break;
+ }
+
+ s = parent;
+ }
+ } while (s != root);
}
/*
* serio_disconnect_port() unbinds a port from its driver. As a side effect
- * all child ports are unbound and destroyed.
+ * all children ports are unbound and destroyed.
*/
static void serio_disconnect_port(struct serio *serio)
{
- struct serio *s, *parent;
+ struct serio *s = serio;
+
+ /*
+ * Children ports should be disconnected and destroyed
+ * first; we travel the tree in depth-first order.
+ */
+ while (!list_empty(&serio->children)) {
+
+ /* Locate a leaf */
+ while (!list_empty(&s->children))
+ s = list_first_entry(&s->children,
+ struct serio, child_node);
- if (serio->child) {
/*
- * Children ports should be disconnected and destroyed
- * first, staring with the leaf one, since we don't want
- * to do recursion
+ * Prune this leaf node unless it is the one we
+ * started with.
*/
- for (s = serio; s->child; s = s->child)
- /* empty */;
-
- do {
- parent = s->parent;
+ if (s != serio) {
+ struct serio *parent = s->parent;
device_release_driver(&s->dev);
serio_destroy_port(s);
- } while ((s = parent) != serio);
+
+ s = parent;
+ }
}
/*
- * Ok, no children left, now disconnect this port
+ * OK, no children left, now disconnect this port.
*/
device_release_driver(&serio->dev);
}
@@ -661,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan);
void serio_reconnect(struct serio *serio)
{
- serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN);
+ serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
}
EXPORT_SYMBOL(serio_reconnect);
@@ -689,14 +724,16 @@ void serio_unregister_port(struct serio *serio)
EXPORT_SYMBOL(serio_unregister_port);
/*
- * Safely unregisters child port if one is present.
+ * Safely unregisters children ports if they are present.
*/
void serio_unregister_child_port(struct serio *serio)
{
+ struct serio *s, *next;
+
mutex_lock(&serio_mutex);
- if (serio->child) {
- serio_disconnect_port(serio->child);
- serio_destroy_port(serio->child);
+ list_for_each_entry_safe(s, next, &serio->children, child_node) {
+ serio_disconnect_port(s);
+ serio_destroy_port(s);
}
mutex_unlock(&serio_mutex);
}
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
index 014248344763..a29a7812bd46 100644
--- a/drivers/input/sparse-keymap.c
+++ b/drivers/input/sparse-keymap.c
@@ -22,6 +22,37 @@ MODULE_DESCRIPTION("Generic support for sparse keymaps");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.1");
+static unsigned int sparse_keymap_get_key_index(struct input_dev *dev,
+ const struct key_entry *k)
+{
+ struct key_entry *key;
+ unsigned int idx = 0;
+
+ for (key = dev->keycode; key->type != KE_END; key++) {
+ if (key->type == KE_KEY) {
+ if (key == k)
+ break;
+ idx++;
+ }
+ }
+
+ return idx;
+}
+
+static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev,
+ unsigned int index)
+{
+ struct key_entry *key;
+ unsigned int key_cnt = 0;
+
+ for (key = dev->keycode; key->type != KE_END; key++)
+ if (key->type == KE_KEY)
+ if (key_cnt++ == index)
+ return key;
+
+ return NULL;
+}
+
/**
* sparse_keymap_entry_from_scancode - perform sparse keymap lookup
* @dev: Input device using sparse keymap
@@ -64,16 +95,36 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
}
EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
+static struct key_entry *sparse_keymap_locate(struct input_dev *dev,
+ const struct input_keymap_entry *ke)
+{
+ struct key_entry *key;
+ unsigned int scancode;
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+ key = sparse_keymap_entry_by_index(dev, ke->index);
+ else if (input_scancode_to_scalar(ke, &scancode) == 0)
+ key = sparse_keymap_entry_from_scancode(dev, scancode);
+ else
+ key = NULL;
+
+ return key;
+}
+
static int sparse_keymap_getkeycode(struct input_dev *dev,
- unsigned int scancode,
- unsigned int *keycode)
+ struct input_keymap_entry *ke)
{
const struct key_entry *key;
if (dev->keycode) {
- key = sparse_keymap_entry_from_scancode(dev, scancode);
+ key = sparse_keymap_locate(dev, ke);
if (key && key->type == KE_KEY) {
- *keycode = key->keycode;
+ ke->keycode = key->keycode;
+ if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
+ ke->index =
+ sparse_keymap_get_key_index(dev, key);
+ ke->len = sizeof(key->code);
+ memcpy(ke->scancode, &key->code, sizeof(key->code));
return 0;
}
}
@@ -82,20 +133,19 @@ static int sparse_keymap_getkeycode(struct input_dev *dev,
}
static int sparse_keymap_setkeycode(struct input_dev *dev,
- unsigned int scancode,
- unsigned int keycode)
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
{
struct key_entry *key;
- int old_keycode;
if (dev->keycode) {
- key = sparse_keymap_entry_from_scancode(dev, scancode);
+ key = sparse_keymap_locate(dev, ke);
if (key && key->type == KE_KEY) {
- old_keycode = key->keycode;
- key->keycode = keycode;
- set_bit(keycode, dev->keybit);
- if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
- clear_bit(old_keycode, dev->keybit);
+ *old_keycode = key->keycode;
+ key->keycode = ke->keycode;
+ set_bit(ke->keycode, dev->keybit);
+ if (!sparse_keymap_entry_from_keycode(dev, *old_keycode))
+ clear_bit(*old_keycode, dev->keybit);
return 0;
}
}
@@ -159,15 +209,14 @@ int sparse_keymap_setup(struct input_dev *dev,
dev->keycode = map;
dev->keycodemax = map_size;
- dev->getkeycode = sparse_keymap_getkeycode;
- dev->setkeycode = sparse_keymap_setkeycode;
+ dev->getkeycode_new = sparse_keymap_getkeycode;
+ dev->setkeycode_new = sparse_keymap_setkeycode;
return 0;
err_out:
kfree(map);
return error;
-
}
EXPORT_SYMBOL(sparse_keymap_setup);
diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig
index effb49ea24aa..58a87755b936 100644
--- a/drivers/input/tablet/Kconfig
+++ b/drivers/input/tablet/Kconfig
@@ -49,6 +49,17 @@ config TABLET_USB_GTCO
To compile this driver as a module, choose M here: the
module will be called gtco.
+config TABLET_USB_HANWANG
+ tristate "Hanwang Art Master III tablet support (USB)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use the USB version of the Hanwang Art
+ Master III tablet.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hanwang.
+
config TABLET_USB_KBTAB
tristate "KB Gear JamStudio tablet support (USB)"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile
index ce8b9a9cfa40..3f6c25220638 100644
--- a/drivers/input/tablet/Makefile
+++ b/drivers/input/tablet/Makefile
@@ -8,5 +8,6 @@ wacom-objs := wacom_wac.o wacom_sys.o
obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o
obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o
+obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c
new file mode 100644
index 000000000000..6504b627b234
--- /dev/null
+++ b/drivers/input/tablet/hanwang.c
@@ -0,0 +1,446 @@
+/*
+ * USB Hanwang tablet support
+ *
+ * Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn>
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_AUTHOR "Xing Wei <weixing@hanwang.com.cn>"
+#define DRIVER_DESC "USB Hanwang tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_HANWANG 0x0b57
+#define HANWANG_TABLET_INT_CLASS 0x0003
+#define HANWANG_TABLET_INT_SUB_CLASS 0x0001
+#define HANWANG_TABLET_INT_PROTOCOL 0x0002
+
+#define ART_MASTER_PKGLEN_MAX 10
+
+/* device IDs */
+#define STYLUS_DEVICE_ID 0x02
+#define TOUCH_DEVICE_ID 0x03
+#define CURSOR_DEVICE_ID 0x06
+#define ERASER_DEVICE_ID 0x0A
+#define PAD_DEVICE_ID 0x0F
+
+/* match vendor and interface info */
+#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR \
+ | USB_DEVICE_ID_MATCH_INT_INFO, \
+ .idVendor = (vend), \
+ .bInterfaceClass = (cl), \
+ .bInterfaceSubClass = (sc), \
+ .bInterfaceProtocol = (pr)
+
+enum hanwang_tablet_type {
+ HANWANG_ART_MASTER_III,
+ HANWANG_ART_MASTER_HD,
+};
+
+struct hanwang {
+ unsigned char *data;
+ dma_addr_t data_dma;
+ struct input_dev *dev;
+ struct usb_device *usbdev;
+ struct urb *irq;
+ const struct hanwang_features *features;
+ unsigned int current_tool;
+ unsigned int current_id;
+ char name[64];
+ char phys[32];
+};
+
+struct hanwang_features {
+ unsigned short pid;
+ char *name;
+ enum hanwang_tablet_type type;
+ int pkg_len;
+ int max_x;
+ int max_y;
+ int max_tilt_x;
+ int max_tilt_y;
+ int max_pressure;
+};
+
+static const struct hanwang_features features_array[] = {
+ { 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III,
+ ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 },
+ { 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III,
+ ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 },
+ { 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III,
+ ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 },
+ { 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD,
+ ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 },
+};
+
+static const int hw_eventtypes[] = {
+ EV_KEY, EV_ABS, EV_MSC,
+};
+
+static const int hw_absevents[] = {
+ ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL,
+ ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC,
+};
+
+static const int hw_btnevents[] = {
+ BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER,
+ BTN_TOOL_MOUSE, BTN_TOOL_FINGER,
+ BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8,
+};
+
+static const int hw_mscevents[] = {
+ MSC_SERIAL,
+};
+
+static void hanwang_parse_packet(struct hanwang *hanwang)
+{
+ unsigned char *data = hanwang->data;
+ struct input_dev *input_dev = hanwang->dev;
+ struct usb_device *dev = hanwang->usbdev;
+ enum hanwang_tablet_type type = hanwang->features->type;
+ int i;
+ u16 x, y, p;
+
+ switch (data[0]) {
+ case 0x02: /* data packet */
+ switch (data[1]) {
+ case 0x80: /* tool prox out */
+ hanwang->current_id = 0;
+ input_report_key(input_dev, hanwang->current_tool, 0);
+ break;
+
+ case 0xc2: /* first time tool prox in */
+ switch (data[3] & 0xf0) {
+ case 0x20: /* art_master III */
+ case 0x30: /* art_master_HD */
+ hanwang->current_id = STYLUS_DEVICE_ID;
+ hanwang->current_tool = BTN_TOOL_PEN;
+ input_report_key(input_dev, BTN_TOOL_PEN, 1);
+ break;
+ case 0xa0: /* art_master III */
+ case 0xb0: /* art_master_HD */
+ hanwang->current_id = ERASER_DEVICE_ID;
+ hanwang->current_tool = BTN_TOOL_RUBBER;
+ input_report_key(input_dev, BTN_TOOL_RUBBER, 1);
+ break;
+ default:
+ hanwang->current_id = 0;
+ dev_dbg(&dev->dev,
+ "unknown tablet tool %02x ", data[0]);
+ break;
+ }
+ break;
+
+ default: /* tool data packet */
+ x = (data[2] << 8) | data[3];
+ y = (data[4] << 8) | data[5];
+
+ switch (type) {
+ case HANWANG_ART_MASTER_III:
+ p = (data[6] << 3) |
+ ((data[7] & 0xc0) >> 5) |
+ (data[1] & 0x01);
+ break;
+
+ case HANWANG_ART_MASTER_HD:
+ p = (data[7] >> 6) | (data[6] << 2);
+ break;
+
+ default:
+ p = 0;
+ break;
+ }
+
+ input_report_abs(input_dev, ABS_X,
+ le16_to_cpup((__le16 *)&x));
+ input_report_abs(input_dev, ABS_Y,
+ le16_to_cpup((__le16 *)&y));
+ input_report_abs(input_dev, ABS_PRESSURE,
+ le16_to_cpup((__le16 *)&p));
+ input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f);
+ input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f);
+ input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02);
+ input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04);
+ break;
+ }
+ input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
+ input_event(input_dev, EV_MSC, MSC_SERIAL,
+ hanwang->features->pid);
+ break;
+
+ case 0x0c:
+ /* roll wheel */
+ hanwang->current_id = PAD_DEVICE_ID;
+
+ switch (type) {
+ case HANWANG_ART_MASTER_III:
+ input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
+ data[2] || data[3]);
+ input_report_abs(input_dev, ABS_WHEEL, data[1]);
+ input_report_key(input_dev, BTN_0, data[2]);
+ for (i = 0; i < 8; i++)
+ input_report_key(input_dev,
+ BTN_1 + i, data[3] & (1 << i));
+ break;
+
+ case HANWANG_ART_MASTER_HD:
+ input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
+ data[2] || data[3] || data[4] ||
+ data[5] || data[6]);
+ input_report_abs(input_dev, ABS_RX,
+ ((data[1] & 0x1f) << 8) | data[2]);
+ input_report_abs(input_dev, ABS_RY,
+ ((data[3] & 0x1f) << 8) | data[4]);
+ input_report_key(input_dev, BTN_0, data[5] & 0x01);
+ for (i = 0; i < 4; i++) {
+ input_report_key(input_dev,
+ BTN_1 + i, data[5] & (1 << i));
+ input_report_key(input_dev,
+ BTN_5 + i, data[6] & (1 << i));
+ }
+ break;
+ }
+
+ input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
+ input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff);
+ break;
+
+ default:
+ dev_dbg(&dev->dev, "error packet %02x ", data[0]);
+ break;
+ }
+
+ input_sync(input_dev);
+}
+
+static void hanwang_irq(struct urb *urb)
+{
+ struct hanwang *hanwang = urb->context;
+ struct usb_device *dev = hanwang->usbdev;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */;
+ hanwang_parse_packet(hanwang);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_err(&dev->dev, "%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dev_err(&dev->dev, "%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ break;
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
+ __func__, retval);
+}
+
+static int hanwang_open(struct input_dev *dev)
+{
+ struct hanwang *hanwang = input_get_drvdata(dev);
+
+ hanwang->irq->dev = hanwang->usbdev;
+ if (usb_submit_urb(hanwang->irq, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void hanwang_close(struct input_dev *dev)
+{
+ struct hanwang *hanwang = input_get_drvdata(dev);
+
+ usb_kill_urb(hanwang->irq);
+}
+
+static bool get_features(struct usb_device *dev, struct hanwang *hanwang)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(features_array); i++) {
+ if (le16_to_cpu(dev->descriptor.idProduct) ==
+ features_array[i].pid) {
+ hanwang->features = &features_array[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct hanwang *hanwang;
+ struct input_dev *input_dev;
+ int error;
+ int i;
+
+ hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!hanwang || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ if (!get_features(dev, hanwang)) {
+ error = -ENXIO;
+ goto fail1;
+ }
+
+ hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len,
+ GFP_KERNEL, &hanwang->data_dma);
+ if (!hanwang->data) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ hanwang->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!hanwang->irq) {
+ error = -ENOMEM;
+ goto fail2;
+ }
+
+ hanwang->usbdev = dev;
+ hanwang->dev = input_dev;
+
+ usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys));
+ strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys));
+
+ strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name));
+ input_dev->name = hanwang->name;
+ input_dev->phys = hanwang->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, hanwang);
+
+ input_dev->open = hanwang_open;
+ input_dev->close = hanwang_close;
+
+ for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i)
+ __set_bit(hw_eventtypes[i], input_dev->evbit);
+
+ for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i)
+ __set_bit(hw_absevents[i], input_dev->absbit);
+
+ for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i)
+ __set_bit(hw_btnevents[i], input_dev->keybit);
+
+ for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i)
+ __set_bit(hw_mscevents[i], input_dev->mscbit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ 0, hanwang->features->max_x, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, hanwang->features->max_y, 4, 0);
+ input_set_abs_params(input_dev, ABS_TILT_X,
+ 0, hanwang->features->max_tilt_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_TILT_Y,
+ 0, hanwang->features->max_tilt_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, hanwang->features->max_pressure, 0, 0);
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(hanwang->irq, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ hanwang->data, hanwang->features->pkg_len,
+ hanwang_irq, hanwang, endpoint->bInterval);
+ hanwang->irq->transfer_dma = hanwang->data_dma;
+ hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ error = input_register_device(hanwang->dev);
+ if (error)
+ goto fail3;
+
+ usb_set_intfdata(intf, hanwang);
+
+ return 0;
+
+ fail3: usb_free_urb(hanwang->irq);
+ fail2: usb_free_coherent(dev, hanwang->features->pkg_len,
+ hanwang->data, hanwang->data_dma);
+ fail1: input_free_device(input_dev);
+ kfree(hanwang);
+ return error;
+
+}
+
+static void hanwang_disconnect(struct usb_interface *intf)
+{
+ struct hanwang *hanwang = usb_get_intfdata(intf);
+
+ input_unregister_device(hanwang->dev);
+ usb_free_urb(hanwang->irq);
+ usb_free_coherent(interface_to_usbdev(intf),
+ hanwang->features->pkg_len, hanwang->data,
+ hanwang->data_dma);
+ kfree(hanwang);
+ usb_set_intfdata(intf, NULL);
+}
+
+static const struct usb_device_id hanwang_ids[] = {
+ { HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS,
+ HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, hanwang_ids);
+
+static struct usb_driver hanwang_driver = {
+ .name = "hanwang",
+ .probe = hanwang_probe,
+ .disconnect = hanwang_disconnect,
+ .id_table = hanwang_ids,
+};
+
+static int __init hanwang_init(void)
+{
+ return usb_register(&hanwang_driver);
+}
+
+static void __exit hanwang_exit(void)
+{
+ usb_deregister(&hanwang_driver);
+}
+
+module_init(hanwang_init);
+module_exit(hanwang_exit);
diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
index 284dfaab6b8c..de5adb109030 100644
--- a/drivers/input/tablet/wacom.h
+++ b/drivers/input/tablet/wacom.h
@@ -118,6 +118,7 @@ struct wacom {
extern const struct usb_device_id wacom_ids[];
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
+void wacom_setup_device_quirks(struct wacom_features *features);
void wacom_setup_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac);
#endif
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index b35876ee6908..fc381498b798 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -120,14 +120,16 @@ static int wacom_open(struct input_dev *dev)
out:
mutex_unlock(&wacom->lock);
- if (retval)
- usb_autopm_put_interface(wacom->intf);
+ usb_autopm_put_interface(wacom->intf);
return retval;
}
static void wacom_close(struct input_dev *dev)
{
struct wacom *wacom = input_get_drvdata(dev);
+ int autopm_error;
+
+ autopm_error = usb_autopm_get_interface(wacom->intf);
mutex_lock(&wacom->lock);
usb_kill_urb(wacom->irq);
@@ -135,7 +137,8 @@ static void wacom_close(struct input_dev *dev)
wacom->intf->needs_remote_wakeup = 0;
mutex_unlock(&wacom->lock);
- usb_autopm_put_interface(wacom->intf);
+ if (!autopm_error)
+ usb_autopm_put_interface(wacom->intf);
}
static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
@@ -196,17 +199,30 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
features->pktlen = WACOM_PKGLEN_TPC2FG;
features->device_type = BTN_TOOL_TRIPLETAP;
}
- features->x_max =
- get_unaligned_le16(&report[i + 3]);
- features->x_phy =
- get_unaligned_le16(&report[i + 6]);
- features->unit = report[i + 9];
- features->unitExpo = report[i + 11];
- i += 12;
+ if (features->type == BAMBOO_PT) {
+ /* need to reset back */
+ features->pktlen = WACOM_PKGLEN_BBTOUCH;
+ features->device_type = BTN_TOOL_TRIPLETAP;
+ features->x_phy =
+ get_unaligned_le16(&report[i + 5]);
+ features->x_max =
+ get_unaligned_le16(&report[i + 8]);
+ i += 15;
+ } else {
+ features->x_max =
+ get_unaligned_le16(&report[i + 3]);
+ features->x_phy =
+ get_unaligned_le16(&report[i + 6]);
+ features->unit = report[i + 9];
+ features->unitExpo = report[i + 11];
+ i += 12;
+ }
} else if (pen) {
/* penabled only accepts exact bytes of data */
if (features->type == TABLETPC2FG)
features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+ if (features->type == BAMBOO_PT)
+ features->pktlen = WACOM_PKGLEN_BBFUN;
features->device_type = BTN_TOOL_PEN;
features->x_max =
get_unaligned_le16(&report[i + 3]);
@@ -235,6 +251,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
features->y_phy =
get_unaligned_le16(&report[i + 6]);
i += 7;
+ } else if (features->type == BAMBOO_PT) {
+ /* need to reset back */
+ features->pktlen = WACOM_PKGLEN_BBTOUCH;
+ features->device_type = BTN_TOOL_TRIPLETAP;
+ features->y_phy =
+ get_unaligned_le16(&report[i + 3]);
+ features->y_max =
+ get_unaligned_le16(&report[i + 6]);
+ i += 12;
} else {
features->y_max =
features->x_max;
@@ -246,6 +271,8 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
/* penabled only accepts exact bytes of data */
if (features->type == TABLETPC2FG)
features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+ if (features->type == BAMBOO_PT)
+ features->pktlen = WACOM_PKGLEN_BBFUN;
features->device_type = BTN_TOOL_PEN;
features->y_max =
get_unaligned_le16(&report[i + 3]);
@@ -296,8 +323,9 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
if (!rep_data)
return error;
- /* ask to report tablet data if it is 2FGT or not a Tablet PC */
- if (features->device_type == BTN_TOOL_TRIPLETAP) {
+ /* ask to report tablet data if it is 2FGT Tablet PC or
+ * not a Tablet PC */
+ if (features->type == TABLETPC2FG) {
do {
rep_data[0] = 3;
rep_data[1] = 4;
@@ -309,7 +337,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
WAC_HID_FEATURE_REPORT, report_id,
rep_data, 3);
} while ((error < 0 || rep_data[1] != 4) && limit++ < 5);
- } else if (features->type != TABLETPC && features->type != TABLETPC2FG) {
+ } else if (features->type != TABLETPC) {
do {
rep_data[0] = 2;
rep_data[1] = 2;
@@ -334,11 +362,16 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
struct usb_host_interface *interface = intf->cur_altsetting;
struct hid_descriptor *hid_desc;
- /* default device to penabled */
+ /* default features */
features->device_type = BTN_TOOL_PEN;
-
- /* only Tablet PCs need to retrieve the info */
- if ((features->type != TABLETPC) && (features->type != TABLETPC2FG))
+ features->x_fuzz = 4;
+ features->y_fuzz = 4;
+ features->pressure_fuzz = 0;
+ features->distance_fuzz = 0;
+
+ /* only Tablet PCs and Bamboo P&T need to retrieve the info */
+ if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
+ (features->type != BAMBOO_PT))
goto out;
if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
@@ -353,12 +386,6 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
if (error)
goto out;
- /* touch device found but size is not defined. use default */
- if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
- features->x_max = 1023;
- features->y_max = 1023;
- }
-
out:
return error;
}
@@ -494,9 +521,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
if (error)
goto fail2;
+ wacom_setup_device_quirks(features);
+
strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
- if (features->type == TABLETPC || features->type == TABLETPC2FG) {
+ if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
/* Append the device type to the name */
strlcat(wacom_wac->name,
features->device_type == BTN_TOOL_PEN ?
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 47fd7a041c52..b3252ef1e279 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -857,6 +857,134 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
return retval;
}
+static int wacom_bpt_touch(struct wacom_wac *wacom)
+{
+ struct wacom_features *features = &wacom->features;
+ struct input_dev *input = wacom->input;
+ unsigned char *data = wacom->data;
+ int sp = 0, sx = 0, sy = 0, count = 0;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ int p = data[9 * i + 2];
+ input_mt_slot(input, i);
+ /*
+ * Touch events need to be disabled while stylus is
+ * in proximity because user's hand is resting on touchpad
+ * and sending unwanted events. User expects tablet buttons
+ * to continue working though.
+ */
+ if (p && !wacom->shared->stylus_in_proximity) {
+ int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
+ int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
+ if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
+ x <<= 5;
+ y <<= 5;
+ }
+ input_report_abs(input, ABS_MT_PRESSURE, p);
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+ if (wacom->id[i] < 0)
+ wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID;
+ if (!count++)
+ sp = p, sx = x, sy = y;
+ } else {
+ wacom->id[i] = -1;
+ }
+ input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]);
+ }
+
+ input_report_key(input, BTN_TOUCH, count > 0);
+ input_report_key(input, BTN_TOOL_FINGER, count == 1);
+ input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2);
+
+ input_report_abs(input, ABS_PRESSURE, sp);
+ input_report_abs(input, ABS_X, sx);
+ input_report_abs(input, ABS_Y, sy);
+
+ input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
+ input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
+ input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
+ input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
+
+ input_sync(input);
+
+ return 0;
+}
+
+static int wacom_bpt_pen(struct wacom_wac *wacom)
+{
+ struct input_dev *input = wacom->input;
+ unsigned char *data = wacom->data;
+ int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
+
+ /*
+ * Similar to Graphire protocol, data[1] & 0x20 is proximity and
+ * data[1] & 0x18 is tool ID. 0x30 is safety check to ignore
+ * 2 unused tool ID's.
+ */
+ prox = (data[1] & 0x30) == 0x30;
+
+ /*
+ * All reports shared between PEN and RUBBER tool must be
+ * forced to a known starting value (zero) when transitioning to
+ * out-of-prox.
+ *
+ * If not reset then, to userspace, it will look like lost events
+ * if new tool comes in-prox with same values as previous tool sent.
+ *
+ * Hardware does report zero in most out-of-prox cases but not all.
+ */
+ if (prox) {
+ if (!wacom->shared->stylus_in_proximity) {
+ if (data[1] & 0x08) {
+ wacom->tool[0] = BTN_TOOL_RUBBER;
+ wacom->id[0] = ERASER_DEVICE_ID;
+ } else {
+ wacom->tool[0] = BTN_TOOL_PEN;
+ wacom->id[0] = STYLUS_DEVICE_ID;
+ }
+ wacom->shared->stylus_in_proximity = true;
+ }
+ x = le16_to_cpup((__le16 *)&data[2]);
+ y = le16_to_cpup((__le16 *)&data[4]);
+ p = le16_to_cpup((__le16 *)&data[6]);
+ d = data[8];
+ pen = data[1] & 0x01;
+ btn1 = data[1] & 0x02;
+ btn2 = data[1] & 0x04;
+ }
+
+ input_report_key(input, BTN_TOUCH, pen);
+ input_report_key(input, BTN_STYLUS, btn1);
+ input_report_key(input, BTN_STYLUS2, btn2);
+
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_PRESSURE, p);
+ input_report_abs(input, ABS_DISTANCE, d);
+
+ if (!prox) {
+ wacom->id[0] = 0;
+ wacom->shared->stylus_in_proximity = false;
+ }
+
+ input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */
+ input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */
+
+ return 1;
+}
+
+static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
+{
+ if (len == WACOM_PKGLEN_BBTOUCH)
+ return wacom_bpt_touch(wacom);
+ else if (len == WACOM_PKGLEN_BBFUN)
+ return wacom_bpt_pen(wacom);
+
+ return 0;
+}
+
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
{
bool sync;
@@ -902,6 +1030,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
sync = wacom_tpc_irq(wacom_wac, len);
break;
+ case BAMBOO_PT:
+ sync = wacom_bpt_irq(wacom_wac, len);
+ break;
+
default:
sync = false;
break;
@@ -911,26 +1043,17 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
input_sync(wacom_wac->input);
}
-static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
+static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
{
struct input_dev *input_dev = wacom_wac->input;
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
- input_set_capability(input_dev, EV_REL, REL_WHEEL);
-
- __set_bit(BTN_LEFT, input_dev->keybit);
- __set_bit(BTN_RIGHT, input_dev->keybit);
- __set_bit(BTN_MIDDLE, input_dev->keybit);
- __set_bit(BTN_SIDE, input_dev->keybit);
- __set_bit(BTN_EXTRA, input_dev->keybit);
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
- __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
__set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
__set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
__set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
- __set_bit(BTN_TOOL_LENS, input_dev->keybit);
__set_bit(BTN_STYLUS, input_dev->keybit);
__set_bit(BTN_STYLUS2, input_dev->keybit);
@@ -939,10 +1062,55 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+}
+
+static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
+{
+ struct input_dev *input_dev = wacom_wac->input;
+
+ input_set_capability(input_dev, EV_REL, REL_WHEEL);
+
+ wacom_setup_cintiq(wacom_wac);
+
+ __set_bit(BTN_LEFT, input_dev->keybit);
+ __set_bit(BTN_RIGHT, input_dev->keybit);
+ __set_bit(BTN_MIDDLE, input_dev->keybit);
+ __set_bit(BTN_SIDE, input_dev->keybit);
+ __set_bit(BTN_EXTRA, input_dev->keybit);
+ __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
+ __set_bit(BTN_TOOL_LENS, input_dev->keybit);
+
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
}
+void wacom_setup_device_quirks(struct wacom_features *features)
+{
+
+ /* touch device found but size is not defined. use default */
+ if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
+ features->x_max = 1023;
+ features->y_max = 1023;
+ }
+
+ /* these device have multiple inputs */
+ if (features->type == TABLETPC || features->type == TABLETPC2FG ||
+ features->type == BAMBOO_PT)
+ features->quirks |= WACOM_QUIRK_MULTI_INPUT;
+
+ /* quirks for bamboo touch */
+ if (features->type == BAMBOO_PT &&
+ features->device_type == BTN_TOOL_TRIPLETAP) {
+ features->x_max <<= 5;
+ features->y_max <<= 5;
+ features->x_fuzz <<= 5;
+ features->y_fuzz <<= 5;
+ features->pressure_max = 256;
+ features->pressure_fuzz = 16;
+ features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
+ }
+}
+
void wacom_setup_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
@@ -953,9 +1121,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOUCH, input_dev->keybit);
- input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
+ features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
+ features->y_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
+ features->pressure_fuzz, 0);
__set_bit(ABS_MISC, input_dev->absbit);
@@ -1005,9 +1176,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_9, input_dev->keybit);
/* fall through */
+ case CINTIQ:
+ for (i = 0; i < 8; i++)
+ __set_bit(BTN_0 + i, input_dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
+ input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
+ input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+ wacom_setup_cintiq(wacom_wac);
+ break;
+
case INTUOS3:
case INTUOS3L:
- case CINTIQ:
__set_bit(BTN_4, input_dev->keybit);
__set_bit(BTN_5, input_dev->keybit);
__set_bit(BTN_6, input_dev->keybit);
@@ -1078,6 +1259,38 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
case PENPARTNER:
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
break;
+
+ case BAMBOO_PT:
+ __clear_bit(ABS_MISC, input_dev->absbit);
+
+ if (features->device_type == BTN_TOOL_TRIPLETAP) {
+ __set_bit(BTN_LEFT, input_dev->keybit);
+ __set_bit(BTN_FORWARD, input_dev->keybit);
+ __set_bit(BTN_BACK, input_dev->keybit);
+ __set_bit(BTN_RIGHT, input_dev->keybit);
+
+ __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+
+ input_mt_create_slots(input_dev, 2);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, features->x_max,
+ features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, features->y_max,
+ features->y_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, features->pressure_max,
+ features->pressure_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
+ MAX_TRACKING_ID, 0, 0);
+ } else if (features->device_type == BTN_TOOL_PEN) {
+ __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+ __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+ __set_bit(BTN_STYLUS, input_dev->keybit);
+ __set_bit(BTN_STYLUS2, input_dev->keybit);
+ }
+ break;
}
}
@@ -1215,6 +1428,14 @@ static const struct wacom_features wacom_features_0xE3 =
{ "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG };
static const struct wacom_features wacom_features_0x47 =
{ "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS };
+static struct wacom_features wacom_features_0xD0 =
+ { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xD1 =
+ { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xD2 =
+ { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xD3 =
+ { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT };
#define USB_DEVICE_WACOM(prod) \
USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \
@@ -1279,6 +1500,10 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xC6) },
{ USB_DEVICE_WACOM(0xC7) },
{ USB_DEVICE_WACOM(0xCE) },
+ { USB_DEVICE_WACOM(0xD0) },
+ { USB_DEVICE_WACOM(0xD1) },
+ { USB_DEVICE_WACOM(0xD2) },
+ { USB_DEVICE_WACOM(0xD3) },
{ USB_DEVICE_WACOM(0xF0) },
{ USB_DEVICE_WACOM(0xCC) },
{ USB_DEVICE_WACOM(0x90) },
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index 99e1a54cd305..00ca01541d89 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -21,6 +21,7 @@
#define WACOM_PKGLEN_INTUOS 10
#define WACOM_PKGLEN_TPC1FG 5
#define WACOM_PKGLEN_TPC2FG 14
+#define WACOM_PKGLEN_BBTOUCH 20
/* device IDs */
#define STYLUS_DEVICE_ID 0x02
@@ -37,6 +38,13 @@
#define WACOM_REPORT_TPC1FG 6
#define WACOM_REPORT_TPC2FG 13
+/* device quirks */
+#define WACOM_QUIRK_MULTI_INPUT 0x0001
+#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
+
+/* largest reported tracking id */
+#define MAX_TRACKING_ID 0xfff
+
enum {
PENPARTNER = 0,
GRAPHIRE,
@@ -44,6 +52,7 @@ enum {
PTU,
PL,
DTU,
+ BAMBOO_PT,
INTUOS,
INTUOS3S,
INTUOS3,
@@ -73,6 +82,11 @@ struct wacom_features {
int y_phy;
unsigned char unit;
unsigned char unitExpo;
+ int x_fuzz;
+ int y_fuzz;
+ int pressure_fuzz;
+ int distance_fuzz;
+ unsigned quirks;
};
struct wacom_shared {
@@ -86,6 +100,7 @@ struct wacom_wac {
int id[3];
__u32 serial[2];
int last_finger;
+ int trk_id;
struct wacom_features features;
struct wacom_shared *shared;
struct input_dev *input;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 0069d9703fda..06ea8da95c62 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -98,6 +98,18 @@ config TOUCHSCREEN_BITSY
To compile this driver as a module, choose M here: the
module will be called h3600_ts_input.
+config TOUCHSCREEN_BU21013
+ tristate "BU21013 based touch panel controllers"
+ depends on I2C
+ help
+ Say Y here if you have a bu21013 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bu21013_ts.
+
config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen"
depends on I2C
@@ -214,6 +226,16 @@ config TOUCHSCREEN_WACOM_W8001
To compile this driver as a module, choose M here: the
module will be called wacom_w8001.
+config TOUCHSCREEN_LPC32XX
+ tristate "LPC32XX touchscreen controller"
+ depends on ARCH_LPC32XX
+ help
+ Say Y here if you have a LPC32XX device and want
+ to support the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lpc32xx_ts.
+
config TOUCHSCREEN_MCS5000
tristate "MELFAS MCS-5000 touchscreen"
depends on I2C
@@ -250,6 +272,18 @@ config TOUCHSCREEN_INEXIO
To compile this driver as a module, choose M here: the
module will be called inexio.
+config TOUCHSCREEN_INTEL_MID
+ tristate "Intel MID platform resistive touchscreen"
+ depends on INTEL_SCU_IPC
+ help
+ Say Y here if you have a Intel MID based touchscreen in
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called intel_mid_touch.
+
config TOUCHSCREEN_MK712
tristate "ICS MicroClock MK712 touchscreen"
help
@@ -328,6 +362,15 @@ config TOUCHSCREEN_MIGOR
To compile this driver as a module, choose M here: the
module will be called migor_ts.
+config TOUCHSCREEN_TNETV107X
+ tristate "TI TNETV107X touchscreen support"
+ depends on ARCH_DAVINCI_TNETV107X
+ help
+ Say Y here if you want to use the TNETV107X touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tnetv107x-ts.
+
config TOUCHSCREEN_TOUCHRIGHT
tristate "Touchright serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 28217e1dcafd..7cc1b4f4b677 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
@@ -23,6 +24,8 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
+obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
@@ -37,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
index 5f0221cffef9..a1952fcc083e 100644
--- a/drivers/input/touchscreen/ad7877.c
+++ b/drivers/input/touchscreen/ad7877.c
@@ -191,13 +191,12 @@ struct ad7877 {
struct spi_message msg;
struct mutex mutex;
- unsigned disabled:1; /* P: mutex */
- unsigned gpio3:1; /* P: mutex */
- unsigned gpio4:1; /* P: mutex */
+ bool disabled; /* P: mutex */
+ bool gpio3; /* P: mutex */
+ bool gpio4; /* P: mutex */
spinlock_t lock;
struct timer_list timer; /* P: lock */
- unsigned pending:1; /* P: lock */
/*
* DMA (thus cache coherency maintenance) requires the
@@ -206,8 +205,8 @@ struct ad7877 {
u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned;
};
-static int gpio3;
-module_param(gpio3, int, 0);
+static bool gpio3;
+module_param(gpio3, bool, 0);
MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
/*
@@ -230,6 +229,7 @@ static int ad7877_read(struct spi_device *spi, u16 reg)
AD7877_READADD(reg));
req->xfer[0].tx_buf = &req->command;
req->xfer[0].len = 2;
+ req->xfer[0].cs_change = 1;
req->xfer[1].rx_buf = &req->sample;
req->xfer[1].len = 2;
@@ -295,20 +295,25 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
req->xfer[0].tx_buf = &req->reset;
req->xfer[0].len = 2;
+ req->xfer[0].cs_change = 1;
req->xfer[1].tx_buf = &req->ref_on;
req->xfer[1].len = 2;
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+ req->xfer[1].cs_change = 1;
req->xfer[2].tx_buf = &req->command;
req->xfer[2].len = 2;
req->xfer[2].delay_usecs = ts->vref_delay_usecs;
+ req->xfer[2].cs_change = 1;
req->xfer[3].rx_buf = &req->sample;
req->xfer[3].len = 2;
+ req->xfer[3].cs_change = 1;
req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/
req->xfer[4].len = 2;
+ req->xfer[4].cs_change = 1;
req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/
req->xfer[5].len = 2;
@@ -327,7 +332,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
return status ? : sample;
}
-static void ad7877_rx(struct ad7877 *ts)
+static int ad7877_process_data(struct ad7877 *ts)
{
struct input_dev *input_dev = ts->input;
unsigned Rt;
@@ -354,11 +359,25 @@ static void ad7877_rx(struct ad7877 *ts)
Rt /= z1;
Rt = (Rt + 2047) >> 12;
+ /*
+ * Sample found inconsistent, pressure is beyond
+ * the maximum. Don't report it to user space.
+ */
+ if (Rt > ts->pressure_max)
+ return -EINVAL;
+
+ if (!timer_pending(&ts->timer))
+ input_report_key(input_dev, BTN_TOUCH, 1);
+
input_report_abs(input_dev, ABS_X, x);
input_report_abs(input_dev, ABS_Y, y);
input_report_abs(input_dev, ABS_PRESSURE, Rt);
input_sync(input_dev);
+
+ return 0;
}
+
+ return -EINVAL;
}
static inline void ad7877_ts_event_release(struct ad7877 *ts)
@@ -366,72 +385,56 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts)
struct input_dev *input_dev = ts->input;
input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_report_key(input_dev, BTN_TOUCH, 0);
input_sync(input_dev);
}
static void ad7877_timer(unsigned long handle)
{
struct ad7877 *ts = (void *)handle;
+ unsigned long flags;
+ spin_lock_irqsave(&ts->lock, flags);
ad7877_ts_event_release(ts);
+ spin_unlock_irqrestore(&ts->lock, flags);
}
static irqreturn_t ad7877_irq(int irq, void *handle)
{
struct ad7877 *ts = handle;
unsigned long flags;
- int status;
+ int error;
- /*
- * The repeated conversion sequencer controlled by TMR kicked off
- * too fast. We ignore the last and process the sample sequence
- * currently in the queue. It can't be older than 9.4ms, and we
- * need to avoid that ts->msg doesn't get issued twice while in work.
- */
+ error = spi_sync(ts->spi, &ts->msg);
+ if (error) {
+ dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
+ goto out;
+ }
spin_lock_irqsave(&ts->lock, flags);
- if (!ts->pending) {
- ts->pending = 1;
-
- status = spi_async(ts->spi, &ts->msg);
- if (status)
- dev_err(&ts->spi->dev, "spi_sync --> %d\n", status);
- }
+ error = ad7877_process_data(ts);
+ if (!error)
+ mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
spin_unlock_irqrestore(&ts->lock, flags);
+out:
return IRQ_HANDLED;
}
-static void ad7877_callback(void *_ts)
-{
- struct ad7877 *ts = _ts;
-
- spin_lock_irq(&ts->lock);
-
- ad7877_rx(ts);
- ts->pending = 0;
- mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
-
- spin_unlock_irq(&ts->lock);
-}
-
static void ad7877_disable(struct ad7877 *ts)
{
mutex_lock(&ts->mutex);
if (!ts->disabled) {
- ts->disabled = 1;
+ ts->disabled = true;
disable_irq(ts->spi->irq);
- /* Wait for spi_async callback */
- while (ts->pending)
- msleep(1);
-
if (del_timer_sync(&ts->timer))
ad7877_ts_event_release(ts);
}
- /* we know the chip's in lowpower mode since we always
+ /*
+ * We know the chip's in lowpower mode since we always
* leave it that way after every request
*/
@@ -443,7 +446,7 @@ static void ad7877_enable(struct ad7877 *ts)
mutex_lock(&ts->mutex);
if (ts->disabled) {
- ts->disabled = 0;
+ ts->disabled = false;
enable_irq(ts->spi->irq);
}
@@ -453,7 +456,7 @@ static void ad7877_enable(struct ad7877 *ts)
#define SHOW(name) static ssize_t \
name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
- struct ad7877 *ts = dev_get_drvdata(dev); \
+ struct ad7877 *ts = dev_get_drvdata(dev); \
ssize_t v = ad7877_read_adc(ts->spi, \
AD7877_READ_CHAN(name)); \
if (v < 0) \
@@ -473,7 +476,7 @@ SHOW(temp2)
static ssize_t ad7877_disable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ad7877 *ts = dev_get_drvdata(dev);
+ struct ad7877 *ts = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", ts->disabled);
}
@@ -503,7 +506,7 @@ static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
static ssize_t ad7877_dac_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ad7877 *ts = dev_get_drvdata(dev);
+ struct ad7877 *ts = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", ts->dac);
}
@@ -533,7 +536,7 @@ static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
static ssize_t ad7877_gpio3_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ad7877 *ts = dev_get_drvdata(dev);
+ struct ad7877 *ts = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", ts->gpio3);
}
@@ -564,7 +567,7 @@ static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
static ssize_t ad7877_gpio4_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ad7877 *ts = dev_get_drvdata(dev);
+ struct ad7877 *ts = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", ts->gpio4);
}
@@ -597,16 +600,35 @@ static struct attribute *ad7877_attributes[] = {
&dev_attr_temp2.attr,
&dev_attr_aux1.attr,
&dev_attr_aux2.attr,
+ &dev_attr_aux3.attr,
&dev_attr_bat1.attr,
&dev_attr_bat2.attr,
&dev_attr_disable.attr,
&dev_attr_dac.attr,
+ &dev_attr_gpio3.attr,
&dev_attr_gpio4.attr,
NULL
};
+static mode_t ad7877_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ mode_t mode = attr->mode;
+
+ if (attr == &dev_attr_aux3.attr) {
+ if (gpio3)
+ mode = 0;
+ } else if (attr == &dev_attr_gpio3.attr) {
+ if (!gpio3)
+ mode = 0;
+ }
+
+ return mode;
+}
+
static const struct attribute_group ad7877_attr_group = {
- .attrs = ad7877_attributes,
+ .is_visible = ad7877_attr_is_visible,
+ .attrs = ad7877_attributes,
};
static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
@@ -635,22 +657,25 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
spi_message_init(m);
- m->complete = ad7877_callback;
m->context = ts;
ts->xfer[0].tx_buf = &ts->cmd_crtl1;
ts->xfer[0].len = 2;
+ ts->xfer[0].cs_change = 1;
spi_message_add_tail(&ts->xfer[0], m);
ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
ts->xfer[1].len = 2;
+ ts->xfer[1].cs_change = 1;
spi_message_add_tail(&ts->xfer[1], m);
- for (i = 0; i < 11; i++) {
+ for (i = 0; i < AD7877_NR_SENSE; i++) {
ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
ts->xfer[i + 2].len = 2;
+ if (i < (AD7877_NR_SENSE - 1))
+ ts->xfer[i + 2].cs_change = 1;
spi_message_add_tail(&ts->xfer[i + 2], m);
}
}
@@ -718,6 +743,8 @@ static int __devinit ad7877_probe(struct spi_device *spi)
input_dev->phys = ts->phys;
input_dev->dev.parent = &spi->dev;
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(ABS_X, input_dev->absbit);
__set_bit(ABS_Y, input_dev->absbit);
@@ -752,8 +779,9 @@ static int __devinit ad7877_probe(struct spi_device *spi)
/* Request AD7877 /DAV GPIO interrupt */
- err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING,
- spi->dev.driver->name, ts);
+ err = request_threaded_irq(spi->irq, NULL, ad7877_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ spi->dev.driver->name, ts);
if (err) {
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
goto err_free_mem;
@@ -763,20 +791,12 @@ static int __devinit ad7877_probe(struct spi_device *spi)
if (err)
goto err_free_irq;
- err = device_create_file(&spi->dev,
- gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
- if (err)
- goto err_remove_attr_group;
-
err = input_register_device(input_dev);
if (err)
- goto err_remove_attr;
+ goto err_remove_attr_group;
return 0;
-err_remove_attr:
- device_remove_file(&spi->dev,
- gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
err_remove_attr_group:
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
err_free_irq:
@@ -790,11 +810,9 @@ err_free_mem:
static int __devexit ad7877_remove(struct spi_device *spi)
{
- struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+ struct ad7877 *ts = dev_get_drvdata(&spi->dev);
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
- device_remove_file(&spi->dev,
- gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
ad7877_disable(ts);
free_irq(ts->spi->irq, ts);
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 16031933a8f6..14ea54b78e46 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -17,9 +17,11 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/types.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/err.h>
+#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
@@ -52,22 +54,23 @@
* files.
*/
-#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */
-#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */
+#define TS_POLL_DELAY 1 /* ms delay before the first sample */
+#define TS_POLL_PERIOD 5 /* ms delay between samples */
/* this driver doesn't aim at the peak continuous sample rate */
#define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)
struct ts_event {
- /* For portability, we can't read 12 bit values using SPI (which
- * would make the controller deliver them as native byteorder u16
+ /*
+ * For portability, we can't read 12 bit values using SPI (which
+ * would make the controller deliver them as native byte order u16
* with msbs zeroed). Instead, we read them as two 8-bit values,
* *** WHICH NEED BYTESWAPPING *** and range adjustment.
*/
u16 x;
u16 y;
u16 z1, z2;
- int ignore;
+ bool ignore;
u8 x_buf[3];
u8 y_buf[3];
};
@@ -110,8 +113,11 @@ struct ads7846 {
struct spi_transfer xfer[18];
struct spi_message msg[5];
- struct spi_message *last_msg;
- int msg_idx;
+ int msg_count;
+ wait_queue_head_t wait;
+
+ bool pendown;
+
int read_cnt;
int read_rep;
int last_read;
@@ -122,14 +128,10 @@ struct ads7846 {
u16 penirq_recheck_delay_usecs;
- spinlock_t lock;
- struct hrtimer timer;
- unsigned pendown:1; /* P: lock */
- unsigned pending:1; /* P: lock */
-// FIXME remove "irq_disabled"
- unsigned irq_disabled:1; /* P: lock */
- unsigned disabled:1;
- unsigned is_suspended:1;
+ struct mutex lock;
+ bool stopped; /* P: lock */
+ bool disabled; /* P: lock */
+ bool suspended; /* P: lock */
int (*filter)(void *data, int data_idx, int *val);
void *filter_data;
@@ -165,7 +167,7 @@ struct ads7846 {
#define ADS_12_BIT (0 << 3)
#define ADS_SER (1 << 2) /* non-differential */
#define ADS_DFR (0 << 2) /* differential */
-#define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */
+#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */
#define ADS_PD10_ADC_ON (1 << 0) /* ADC on */
#define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */
#define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */
@@ -193,6 +195,78 @@ struct ads7846 {
#define REF_ON (READ_12BIT_DFR(x, 1, 1))
#define REF_OFF (READ_12BIT_DFR(y, 0, 0))
+/* Must be called with ts->lock held */
+static void ads7846_stop(struct ads7846 *ts)
+{
+ if (!ts->disabled && !ts->suspended) {
+ /* Signal IRQ thread to stop polling and disable the handler. */
+ ts->stopped = true;
+ mb();
+ wake_up(&ts->wait);
+ disable_irq(ts->spi->irq);
+ }
+}
+
+/* Must be called with ts->lock held */
+static void ads7846_restart(struct ads7846 *ts)
+{
+ if (!ts->disabled && !ts->suspended) {
+ /* Tell IRQ thread that it may poll the device. */
+ ts->stopped = false;
+ mb();
+ enable_irq(ts->spi->irq);
+ }
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_disable(struct ads7846 *ts)
+{
+ ads7846_stop(ts);
+ regulator_disable(ts->reg);
+
+ /*
+ * We know the chip's in low power mode since we always
+ * leave it that way after every request
+ */
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_enable(struct ads7846 *ts)
+{
+ regulator_enable(ts->reg);
+ ads7846_restart(ts);
+}
+
+static void ads7846_disable(struct ads7846 *ts)
+{
+ mutex_lock(&ts->lock);
+
+ if (!ts->disabled) {
+
+ if (!ts->suspended)
+ __ads7846_disable(ts);
+
+ ts->disabled = true;
+ }
+
+ mutex_unlock(&ts->lock);
+}
+
+static void ads7846_enable(struct ads7846 *ts)
+{
+ mutex_lock(&ts->lock);
+
+ if (ts->disabled) {
+
+ ts->disabled = false;
+
+ if (!ts->suspended)
+ __ads7846_enable(ts);
+ }
+
+ mutex_unlock(&ts->lock);
+}
+
/*--------------------------------------------------------------------------*/
/*
@@ -219,23 +293,15 @@ struct ads7845_ser_req {
struct spi_transfer xfer[2];
};
-static void ads7846_enable(struct ads7846 *ts);
-static void ads7846_disable(struct ads7846 *ts);
-
-static int device_suspended(struct device *dev)
-{
- struct ads7846 *ts = dev_get_drvdata(dev);
- return ts->is_suspended || ts->disabled;
-}
-
static int ads7846_read12_ser(struct device *dev, unsigned command)
{
- struct spi_device *spi = to_spi_device(dev);
- struct ads7846 *ts = dev_get_drvdata(dev);
- struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL);
- int status;
- int use_internal;
+ struct spi_device *spi = to_spi_device(dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ struct ser_req *req;
+ int status;
+ int use_internal;
+ req = kzalloc(sizeof *req, GFP_KERNEL);
if (!req)
return -ENOMEM;
@@ -282,11 +348,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
CS_CHANGE(req->xfer[5]);
spi_message_add_tail(&req->xfer[5], &req->msg);
- ts->irq_disabled = 1;
- disable_irq(spi->irq);
+ mutex_lock(&ts->lock);
+ ads7846_stop(ts);
status = spi_sync(spi, &req->msg);
- ts->irq_disabled = 0;
- enable_irq(spi->irq);
+ ads7846_restart(ts);
+ mutex_unlock(&ts->lock);
if (status == 0) {
/* on-wire is a must-ignore bit, a BE12 value, then padding */
@@ -301,11 +367,12 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
static int ads7845_read12_ser(struct device *dev, unsigned command)
{
- struct spi_device *spi = to_spi_device(dev);
- struct ads7846 *ts = dev_get_drvdata(dev);
- struct ads7845_ser_req *req = kzalloc(sizeof *req, GFP_KERNEL);
- int status;
+ struct spi_device *spi = to_spi_device(dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ struct ads7845_ser_req *req;
+ int status;
+ req = kzalloc(sizeof *req, GFP_KERNEL);
if (!req)
return -ENOMEM;
@@ -317,11 +384,11 @@ static int ads7845_read12_ser(struct device *dev, unsigned command)
req->xfer[0].len = 3;
spi_message_add_tail(&req->xfer[0], &req->msg);
- ts->irq_disabled = 1;
- disable_irq(spi->irq);
+ mutex_lock(&ts->lock);
+ ads7846_stop(ts);
status = spi_sync(spi, &req->msg);
- ts->irq_disabled = 0;
- enable_irq(spi->irq);
+ ads7846_restart(ts);
+ mutex_unlock(&ts->lock);
if (status == 0) {
/* BE12 value, then padding */
@@ -374,6 +441,7 @@ static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
/* external resistors may scale vAUX into 0..vREF */
retval *= ts->vref_mv;
retval = retval >> 12;
+
return retval;
}
@@ -384,13 +452,13 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
/* ads7846 has a resistor ladder to scale this signal down */
if (ts->model == 7846)
retval *= 4;
+
return retval;
}
SHOW(in0_input, vaux, vaux_adjust)
SHOW(in1_input, vbatt, vbatt_adjust)
-
static struct attribute *ads7846_attributes[] = {
&dev_attr_temp0.attr,
&dev_attr_temp1.attr,
@@ -498,17 +566,12 @@ static inline void ads784x_hwmon_unregister(struct spi_device *spi,
}
#endif
-static int is_pen_down(struct device *dev)
-{
- struct ads7846 *ts = dev_get_drvdata(dev);
-
- return ts->pendown;
-}
-
static ssize_t ads7846_pen_down_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%u\n", is_pen_down(dev));
+ struct ads7846 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->pendown);
}
static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);
@@ -516,7 +579,7 @@ static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);
static ssize_t ads7846_disable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct ads7846 *ts = dev_get_drvdata(dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", ts->disabled);
}
@@ -531,15 +594,11 @@ static ssize_t ads7846_disable_store(struct device *dev,
if (strict_strtoul(buf, 10, &i))
return -EINVAL;
- spin_lock_irq(&ts->lock);
-
if (i)
ads7846_disable(ts);
else
ads7846_enable(ts);
- spin_unlock_irq(&ts->lock);
-
return count;
}
@@ -569,23 +628,141 @@ static void null_wait_for_sync(void)
{
}
-/*
- * PENIRQ only kicks the timer. The timer only reissues the SPI transfer,
- * to retrieve touchscreen status.
- *
- * The SPI transfer completion callback does the real work. It reports
- * touchscreen events and reactivates the timer (or IRQ) as appropriate.
- */
+static int ads7846_debounce_filter(void *ads, int data_idx, int *val)
+{
+ struct ads7846 *ts = ads;
+
+ if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
+ /* Start over collecting consistent readings. */
+ ts->read_rep = 0;
+ /*
+ * Repeat it, if this was the first read or the read
+ * wasn't consistent enough.
+ */
+ if (ts->read_cnt < ts->debounce_max) {
+ ts->last_read = *val;
+ ts->read_cnt++;
+ return ADS7846_FILTER_REPEAT;
+ } else {
+ /*
+ * Maximum number of debouncing reached and still
+ * not enough number of consistent readings. Abort
+ * the whole sample, repeat it in the next sampling
+ * period.
+ */
+ ts->read_cnt = 0;
+ return ADS7846_FILTER_IGNORE;
+ }
+ } else {
+ if (++ts->read_rep > ts->debounce_rep) {
+ /*
+ * Got a good reading for this coordinate,
+ * go for the next one.
+ */
+ ts->read_cnt = 0;
+ ts->read_rep = 0;
+ return ADS7846_FILTER_OK;
+ } else {
+ /* Read more values that are consistent. */
+ ts->read_cnt++;
+ return ADS7846_FILTER_REPEAT;
+ }
+ }
+}
+
+static int ads7846_no_filter(void *ads, int data_idx, int *val)
+{
+ return ADS7846_FILTER_OK;
+}
+
+static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m)
+{
+ struct spi_transfer *t =
+ list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+ if (ts->model == 7845) {
+ return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
+ } else {
+ /*
+ * adjust: on-wire is a must-ignore bit, a BE12 value, then
+ * padding; built from two 8 bit values written msb-first.
+ */
+ return be16_to_cpup((__be16 *)t->rx_buf) >> 3;
+ }
+}
+
+static void ads7846_update_value(struct spi_message *m, int val)
+{
+ struct spi_transfer *t =
+ list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+ *(u16 *)t->rx_buf = val;
+}
+
+static void ads7846_read_state(struct ads7846 *ts)
+{
+ struct ads7846_packet *packet = ts->packet;
+ struct spi_message *m;
+ int msg_idx = 0;
+ int val;
+ int action;
+ int error;
+
+ while (msg_idx < ts->msg_count) {
+
+ ts->wait_for_sync();
+
+ m = &ts->msg[msg_idx];
+ error = spi_sync(ts->spi, m);
+ if (error) {
+ dev_err(&ts->spi->dev, "spi_async --> %d\n", error);
+ packet->tc.ignore = true;
+ return;
+ }
+
+ /*
+ * Last message is power down request, no need to convert
+ * or filter the value.
+ */
+ if (msg_idx < ts->msg_count - 1) {
-static void ads7846_rx(void *ads)
+ val = ads7846_get_value(ts, m);
+
+ action = ts->filter(ts->filter_data, msg_idx, &val);
+ switch (action) {
+ case ADS7846_FILTER_REPEAT:
+ continue;
+
+ case ADS7846_FILTER_IGNORE:
+ packet->tc.ignore = true;
+ msg_idx = ts->msg_count - 1;
+ continue;
+
+ case ADS7846_FILTER_OK:
+ ads7846_update_value(m, val);
+ packet->tc.ignore = false;
+ msg_idx++;
+ break;
+
+ default:
+ BUG();
+ }
+ } else {
+ msg_idx++;
+ }
+ }
+}
+
+static void ads7846_report_state(struct ads7846 *ts)
{
- struct ads7846 *ts = ads;
- struct ads7846_packet *packet = ts->packet;
- unsigned Rt;
- u16 x, y, z1, z2;
+ struct ads7846_packet *packet = ts->packet;
+ unsigned int Rt;
+ u16 x, y, z1, z2;
- /* ads7846_rx_val() did in-place conversion (including byteswap) from
- * on-the-wire format as part of debouncing to get stable readings.
+ /*
+ * ads7846_get_value() does in-place conversion (including byte swap)
+ * from on-the-wire format as part of debouncing to get stable
+ * readings.
*/
if (ts->model == 7845) {
x = *(u16 *)packet->tc.x_buf;
@@ -623,19 +800,19 @@ static void ads7846_rx(void *ads)
Rt = 0;
}
- /* Sample found inconsistent by debouncing or pressure is beyond
+ /*
+ * Sample found inconsistent by debouncing or pressure is beyond
* the maximum. Don't report it to user space, repeat at least
* once more the measurement
*/
if (packet->tc.ignore || Rt > ts->pressure_max) {
dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
packet->tc.ignore, Rt);
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
return;
}
- /* Maybe check the pendown state before reporting. This discards
+ /*
+ * Maybe check the pendown state before reporting. This discards
* false readings when the pen is lifted.
*/
if (ts->penirq_recheck_delay_usecs) {
@@ -644,8 +821,9 @@ static void ads7846_rx(void *ads)
Rt = 0;
}
- /* NOTE: We can't rely on the pressure to determine the pen down
- * state, even this controller has a pressure sensor. The pressure
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
+ * state, even this controller has a pressure sensor. The pressure
* value can fluctuate for quite a while after lifting the pen and
* in some cases may not even settle at the expected value.
*
@@ -655,15 +833,15 @@ static void ads7846_rx(void *ads)
if (Rt) {
struct input_dev *input = ts->input;
+ if (ts->swap_xy)
+ swap(x, y);
+
if (!ts->pendown) {
input_report_key(input, BTN_TOUCH, 1);
- ts->pendown = 1;
+ ts->pendown = true;
dev_vdbg(&ts->spi->dev, "DOWN\n");
}
- if (ts->swap_xy)
- swap(x, y);
-
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
@@ -671,246 +849,94 @@ static void ads7846_rx(void *ads)
input_sync(input);
dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
}
-
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
-}
-
-static int ads7846_debounce(void *ads, int data_idx, int *val)
-{
- struct ads7846 *ts = ads;
-
- if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
- /* Start over collecting consistent readings. */
- ts->read_rep = 0;
- /* Repeat it, if this was the first read or the read
- * wasn't consistent enough. */
- if (ts->read_cnt < ts->debounce_max) {
- ts->last_read = *val;
- ts->read_cnt++;
- return ADS7846_FILTER_REPEAT;
- } else {
- /* Maximum number of debouncing reached and still
- * not enough number of consistent readings. Abort
- * the whole sample, repeat it in the next sampling
- * period.
- */
- ts->read_cnt = 0;
- return ADS7846_FILTER_IGNORE;
- }
- } else {
- if (++ts->read_rep > ts->debounce_rep) {
- /* Got a good reading for this coordinate,
- * go for the next one. */
- ts->read_cnt = 0;
- ts->read_rep = 0;
- return ADS7846_FILTER_OK;
- } else {
- /* Read more values that are consistent. */
- ts->read_cnt++;
- return ADS7846_FILTER_REPEAT;
- }
- }
}
-static int ads7846_no_filter(void *ads, int data_idx, int *val)
+static irqreturn_t ads7846_hard_irq(int irq, void *handle)
{
- return ADS7846_FILTER_OK;
-}
-
-static void ads7846_rx_val(void *ads)
-{
- struct ads7846 *ts = ads;
- struct ads7846_packet *packet = ts->packet;
- struct spi_message *m;
- struct spi_transfer *t;
- int val;
- int action;
- int status;
-
- m = &ts->msg[ts->msg_idx];
- t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
-
- if (ts->model == 7845) {
- val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
- } else {
- /* adjust: on-wire is a must-ignore bit, a BE12 value, then
- * padding; built from two 8 bit values written msb-first.
- */
- val = be16_to_cpup((__be16 *)t->rx_buf) >> 3;
- }
+ struct ads7846 *ts = handle;
- action = ts->filter(ts->filter_data, ts->msg_idx, &val);
- switch (action) {
- case ADS7846_FILTER_REPEAT:
- break;
- case ADS7846_FILTER_IGNORE:
- packet->tc.ignore = 1;
- /* Last message will contain ads7846_rx() as the
- * completion function.
- */
- m = ts->last_msg;
- break;
- case ADS7846_FILTER_OK:
- *(u16 *)t->rx_buf = val;
- packet->tc.ignore = 0;
- m = &ts->msg[++ts->msg_idx];
- break;
- default:
- BUG();
- }
- ts->wait_for_sync();
- status = spi_async(ts->spi, m);
- if (status)
- dev_err(&ts->spi->dev, "spi_async --> %d\n",
- status);
+ return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
}
-static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
-{
- struct ads7846 *ts = container_of(handle, struct ads7846, timer);
- int status = 0;
-
- spin_lock(&ts->lock);
-
- if (unlikely(!get_pendown_state(ts) ||
- device_suspended(&ts->spi->dev))) {
- if (ts->pendown) {
- struct input_dev *input = ts->input;
-
- input_report_key(input, BTN_TOUCH, 0);
- input_report_abs(input, ABS_PRESSURE, 0);
- input_sync(input);
-
- ts->pendown = 0;
- dev_vdbg(&ts->spi->dev, "UP\n");
- }
-
- /* measurement cycle ended */
- if (!device_suspended(&ts->spi->dev)) {
- ts->irq_disabled = 0;
- enable_irq(ts->spi->irq);
- }
- ts->pending = 0;
- } else {
- /* pen is still down, continue with the measurement */
- ts->msg_idx = 0;
- ts->wait_for_sync();
- status = spi_async(ts->spi, &ts->msg[0]);
- if (status)
- dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
- }
-
- spin_unlock(&ts->lock);
- return HRTIMER_NORESTART;
-}
static irqreturn_t ads7846_irq(int irq, void *handle)
{
struct ads7846 *ts = handle;
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
- if (likely(get_pendown_state(ts))) {
- if (!ts->irq_disabled) {
- /* The ARM do_simple_IRQ() dispatcher doesn't act
- * like the other dispatchers: it will report IRQs
- * even after they've been disabled. We work around
- * that here. (The "generic irq" framework may help...)
- */
- ts->irq_disabled = 1;
- disable_irq_nosync(ts->spi->irq);
- ts->pending = 1;
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
- HRTIMER_MODE_REL);
- }
- }
- spin_unlock_irqrestore(&ts->lock, flags);
- return IRQ_HANDLED;
-}
+ /* Start with a small delay before checking pendown state */
+ msleep(TS_POLL_DELAY);
-/*--------------------------------------------------------------------------*/
+ while (!ts->stopped && get_pendown_state(ts)) {
-/* Must be called with ts->lock held */
-static void ads7846_disable(struct ads7846 *ts)
-{
- if (ts->disabled)
- return;
+ /* pen is down, continue with the measurement */
+ ads7846_read_state(ts);
- ts->disabled = 1;
+ if (!ts->stopped)
+ ads7846_report_state(ts);
- /* are we waiting for IRQ, or polling? */
- if (!ts->pending) {
- ts->irq_disabled = 1;
- disable_irq(ts->spi->irq);
- } else {
- /* the timer will run at least once more, and
- * leave everything in a clean state, IRQ disabled
- */
- while (ts->pending) {
- spin_unlock_irq(&ts->lock);
- msleep(1);
- spin_lock_irq(&ts->lock);
- }
+ wait_event_timeout(ts->wait, ts->stopped,
+ msecs_to_jiffies(TS_POLL_PERIOD));
}
- regulator_disable(ts->reg);
-
- /* we know the chip's in lowpower mode since we always
- * leave it that way after every request
- */
-}
+ if (ts->pendown) {
+ struct input_dev *input = ts->input;
-/* Must be called with ts->lock held */
-static void ads7846_enable(struct ads7846 *ts)
-{
- if (!ts->disabled)
- return;
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
- regulator_enable(ts->reg);
+ ts->pendown = false;
+ dev_vdbg(&ts->spi->dev, "UP\n");
+ }
- ts->disabled = 0;
- ts->irq_disabled = 0;
- enable_irq(ts->spi->irq);
+ return IRQ_HANDLED;
}
static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
{
struct ads7846 *ts = dev_get_drvdata(&spi->dev);
- spin_lock_irq(&ts->lock);
+ mutex_lock(&ts->lock);
- ts->is_suspended = 1;
- ads7846_disable(ts);
+ if (!ts->suspended) {
- spin_unlock_irq(&ts->lock);
+ if (!ts->disabled)
+ __ads7846_disable(ts);
- if (device_may_wakeup(&ts->spi->dev))
- enable_irq_wake(ts->spi->irq);
+ if (device_may_wakeup(&ts->spi->dev))
+ enable_irq_wake(ts->spi->irq);
- return 0;
+ ts->suspended = true;
+ }
+
+ mutex_unlock(&ts->lock);
+ return 0;
}
static int ads7846_resume(struct spi_device *spi)
{
struct ads7846 *ts = dev_get_drvdata(&spi->dev);
- if (device_may_wakeup(&ts->spi->dev))
- disable_irq_wake(ts->spi->irq);
+ mutex_lock(&ts->lock);
+
+ if (ts->suspended) {
- spin_lock_irq(&ts->lock);
+ ts->suspended = false;
- ts->is_suspended = 0;
- ads7846_enable(ts);
+ if (device_may_wakeup(&ts->spi->dev))
+ disable_irq_wake(ts->spi->irq);
- spin_unlock_irq(&ts->lock);
+ if (!ts->disabled)
+ __ads7846_enable(ts);
+ }
+
+ mutex_unlock(&ts->lock);
return 0;
}
-static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
+static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts)
{
struct ads7846_platform_data *pdata = spi->dev.platform_data;
int err;
@@ -932,146 +958,40 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
err = gpio_request(pdata->gpio_pendown, "ads7846_pendown");
if (err) {
dev_err(&spi->dev, "failed to request pendown GPIO%d\n",
- pdata->gpio_pendown);
+ pdata->gpio_pendown);
return err;
}
ts->gpio_pendown = pdata->gpio_pendown;
+
return 0;
}
-static int __devinit ads7846_probe(struct spi_device *spi)
+/*
+ * Set up the transfers to read touchscreen state; this assumes we
+ * use formula #2 for pressure, not #3.
+ */
+static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts,
+ const struct ads7846_platform_data *pdata)
{
- struct ads7846 *ts;
- struct ads7846_packet *packet;
- struct input_dev *input_dev;
- const struct ads7846_platform_data *pdata = spi->dev.platform_data;
- struct spi_message *m;
- struct spi_transfer *x;
- unsigned long irq_flags;
- int vref;
- int err;
-
- if (!spi->irq) {
- dev_dbg(&spi->dev, "no IRQ?\n");
- return -ENODEV;
- }
-
- if (!pdata) {
- dev_dbg(&spi->dev, "no platform data?\n");
- return -ENODEV;
- }
-
- /* don't exceed max specified sample rate */
- if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
- dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
- (spi->max_speed_hz/SAMPLE_BITS)/1000);
- return -EINVAL;
- }
-
- /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except
- * that even if the hardware can do that, the SPI controller driver
- * may not. So we stick to very-portable 8 bit words, both RX and TX.
- */
- spi->bits_per_word = 8;
- spi->mode = SPI_MODE_0;
- err = spi_setup(spi);
- if (err < 0)
- return err;
-
- ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
- packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ts || !packet || !input_dev) {
- err = -ENOMEM;
- goto err_free_mem;
- }
-
- dev_set_drvdata(&spi->dev, ts);
-
- ts->packet = packet;
- ts->spi = spi;
- ts->input = input_dev;
- ts->vref_mv = pdata->vref_mv;
- ts->swap_xy = pdata->swap_xy;
-
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- ts->timer.function = ads7846_timer;
-
- spin_lock_init(&ts->lock);
-
- ts->model = pdata->model ? : 7846;
- ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
- ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
- ts->pressure_max = pdata->pressure_max ? : ~0;
-
- if (pdata->filter != NULL) {
- if (pdata->filter_init != NULL) {
- err = pdata->filter_init(pdata, &ts->filter_data);
- if (err < 0)
- goto err_free_mem;
- }
- ts->filter = pdata->filter;
- ts->filter_cleanup = pdata->filter_cleanup;
- } else if (pdata->debounce_max) {
- ts->debounce_max = pdata->debounce_max;
- if (ts->debounce_max < 2)
- ts->debounce_max = 2;
- ts->debounce_tol = pdata->debounce_tol;
- ts->debounce_rep = pdata->debounce_rep;
- ts->filter = ads7846_debounce;
- ts->filter_data = ts;
- } else
- ts->filter = ads7846_no_filter;
-
- err = setup_pendown(spi, ts);
- if (err)
- goto err_cleanup_filter;
-
- if (pdata->penirq_recheck_delay_usecs)
- ts->penirq_recheck_delay_usecs =
- pdata->penirq_recheck_delay_usecs;
-
- ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
-
- snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
- snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
-
- input_dev->name = ts->name;
- input_dev->phys = ts->phys;
- input_dev->dev.parent = &spi->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,
- pdata->x_min ? : 0,
- pdata->x_max ? : MAX_12BIT,
- 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- pdata->y_min ? : 0,
- pdata->y_max ? : MAX_12BIT,
- 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE,
- pdata->pressure_min, pdata->pressure_max, 0, 0);
-
- vref = pdata->keep_vref_on;
+ struct spi_message *m = &ts->msg[0];
+ struct spi_transfer *x = ts->xfer;
+ struct ads7846_packet *packet = ts->packet;
+ int vref = pdata->keep_vref_on;
if (ts->model == 7873) {
- /* The AD7873 is almost identical to the ADS7846
+ /*
+ * The AD7873 is almost identical to the ADS7846
* keep VREF off during differential/ratiometric
- * conversion modes
+ * conversion modes.
*/
ts->model = 7846;
vref = 0;
}
- /* set up the transfers to read touchscreen state; this assumes we
- * use formula #2 for pressure, not #3.
- */
- m = &ts->msg[0];
- x = ts->xfer;
-
+ ts->msg_count = 1;
spi_message_init(m);
+ m->context = ts;
if (ts->model == 7845) {
packet->read_y_cmd[0] = READ_Y(vref);
@@ -1094,7 +1014,8 @@ static int __devinit ads7846_probe(struct spi_device *spi)
spi_message_add_tail(x, m);
}
- /* the first sample after switching drivers can be low quality;
+ /*
+ * The first sample after switching drivers can be low quality;
* optionally discard it, using a second one after the signals
* have had enough time to stabilize.
*/
@@ -1112,11 +1033,10 @@ static int __devinit ads7846_probe(struct spi_device *spi)
spi_message_add_tail(x, m);
}
- m->complete = ads7846_rx_val;
- m->context = ts;
-
+ ts->msg_count++;
m++;
spi_message_init(m);
+ m->context = ts;
if (ts->model == 7845) {
x++;
@@ -1156,13 +1076,12 @@ static int __devinit ads7846_probe(struct spi_device *spi)
spi_message_add_tail(x, m);
}
- m->complete = ads7846_rx_val;
- m->context = ts;
-
/* turn y+ off, x- on; we'll use formula #2 */
if (ts->model == 7846) {
+ ts->msg_count++;
m++;
spi_message_init(m);
+ m->context = ts;
x++;
packet->read_z1 = READ_Z1(vref);
@@ -1190,11 +1109,10 @@ static int __devinit ads7846_probe(struct spi_device *spi)
spi_message_add_tail(x, m);
}
- m->complete = ads7846_rx_val;
- m->context = ts;
-
+ ts->msg_count++;
m++;
spi_message_init(m);
+ m->context = ts;
x++;
packet->read_z2 = READ_Z2(vref);
@@ -1221,14 +1139,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
x->len = 2;
spi_message_add_tail(x, m);
}
-
- m->complete = ads7846_rx_val;
- m->context = ts;
}
/* power down */
+ ts->msg_count++;
m++;
spi_message_init(m);
+ m->context = ts;
if (ts->model == 7845) {
x++;
@@ -1251,11 +1168,119 @@ static int __devinit ads7846_probe(struct spi_device *spi)
CS_CHANGE(*x);
spi_message_add_tail(x, m);
+}
- m->complete = ads7846_rx;
- m->context = ts;
+static int __devinit ads7846_probe(struct spi_device *spi)
+{
+ struct ads7846 *ts;
+ struct ads7846_packet *packet;
+ struct input_dev *input_dev;
+ struct ads7846_platform_data *pdata = spi->dev.platform_data;
+ unsigned long irq_flags;
+ int err;
+
+ if (!spi->irq) {
+ dev_dbg(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ /* don't exceed max specified sample rate */
+ if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
+ dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
+ (spi->max_speed_hz/SAMPLE_BITS)/1000);
+ return -EINVAL;
+ }
+
+ /* We'd set TX word size 8 bits and RX word size to 13 bits ... except
+ * that even if the hardware can do that, the SPI controller driver
+ * may not. So we stick to very-portable 8 bit words, both RX and TX.
+ */
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
- ts->last_msg = m;
+ ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
+ packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !packet || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ dev_set_drvdata(&spi->dev, ts);
+
+ ts->packet = packet;
+ ts->spi = spi;
+ ts->input = input_dev;
+ ts->vref_mv = pdata->vref_mv;
+ ts->swap_xy = pdata->swap_xy;
+
+ mutex_init(&ts->lock);
+ init_waitqueue_head(&ts->wait);
+
+ ts->model = pdata->model ? : 7846;
+ ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+ ts->pressure_max = pdata->pressure_max ? : ~0;
+
+ if (pdata->filter != NULL) {
+ if (pdata->filter_init != NULL) {
+ err = pdata->filter_init(pdata, &ts->filter_data);
+ if (err < 0)
+ goto err_free_mem;
+ }
+ ts->filter = pdata->filter;
+ ts->filter_cleanup = pdata->filter_cleanup;
+ } else if (pdata->debounce_max) {
+ ts->debounce_max = pdata->debounce_max;
+ if (ts->debounce_max < 2)
+ ts->debounce_max = 2;
+ ts->debounce_tol = pdata->debounce_tol;
+ ts->debounce_rep = pdata->debounce_rep;
+ ts->filter = ads7846_debounce_filter;
+ ts->filter_data = ts;
+ } else {
+ ts->filter = ads7846_no_filter;
+ }
+
+ err = ads7846_setup_pendown(spi, ts);
+ if (err)
+ goto err_cleanup_filter;
+
+ if (pdata->penirq_recheck_delay_usecs)
+ ts->penirq_recheck_delay_usecs =
+ pdata->penirq_recheck_delay_usecs;
+
+ ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+ snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
+
+ input_dev->name = ts->name;
+ input_dev->phys = ts->phys;
+ input_dev->dev.parent = &spi->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,
+ pdata->x_min ? : 0,
+ pdata->x_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+ ads7846_setup_spi_msg(ts, pdata);
ts->reg = regulator_get(&spi->dev, "vcc");
if (IS_ERR(ts->reg)) {
@@ -1271,16 +1296,17 @@ static int __devinit ads7846_probe(struct spi_device *spi)
}
irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING;
+ irq_flags |= IRQF_ONESHOT;
- err = request_irq(spi->irq, ads7846_irq, irq_flags,
- spi->dev.driver->name, ts);
-
+ err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq,
+ irq_flags, spi->dev.driver->name, ts);
if (err && !pdata->irq_flags) {
dev_info(&spi->dev,
"trying pin change workaround on irq %d\n", spi->irq);
- err = request_irq(spi->irq, ads7846_irq,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- spi->dev.driver->name, ts);
+ irq_flags |= IRQF_TRIGGER_RISING;
+ err = request_threaded_irq(spi->irq,
+ ads7846_hard_irq, ads7846_irq,
+ irq_flags, spi->dev.driver->name, ts);
}
if (err) {
@@ -1294,7 +1320,8 @@ static int __devinit ads7846_probe(struct spi_device *spi)
dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
- /* take a first sample, leaving nPENIRQ active and vREF off; avoid
+ /*
+ * Take a first sample, leaving nPENIRQ active and vREF off; avoid
* the touchscreen, in case it's not connected.
*/
if (ts->model == 7845)
@@ -1340,20 +1367,18 @@ static int __devinit ads7846_probe(struct spi_device *spi)
static int __devexit ads7846_remove(struct spi_device *spi)
{
- struct ads7846 *ts = dev_get_drvdata(&spi->dev);
+ struct ads7846 *ts = dev_get_drvdata(&spi->dev);
device_init_wakeup(&spi->dev, false);
- ads784x_hwmon_unregister(spi, ts);
- input_unregister_device(ts->input);
-
- ads7846_suspend(spi, PMSG_SUSPEND);
-
sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+ ads7846_disable(ts);
free_irq(ts->spi->irq, ts);
- /* suspend left the IRQ disabled */
- enable_irq(ts->spi->irq);
+
+ input_unregister_device(ts->input);
+
+ ads784x_hwmon_unregister(spi, ts);
regulator_disable(ts->reg);
regulator_put(ts->reg);
@@ -1368,6 +1393,7 @@ static int __devexit ads7846_remove(struct spi_device *spi)
kfree(ts);
dev_dbg(&spi->dev, "unregistered touchscreen\n");
+
return 0;
}
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
new file mode 100644
index 000000000000..ccde58602563
--- /dev/null
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/input/bu21013.h>
+#include <linux/slab.h>
+
+#define PEN_DOWN_INTR 0
+#define MAX_FINGERS 2
+#define RESET_DELAY 30
+#define PENUP_TIMEOUT (10)
+#define DELTA_MIN 16
+#define MASK_BITS 0x03
+#define SHIFT_8 8
+#define SHIFT_2 2
+#define LENGTH_OF_BUFFER 11
+#define I2C_RETRY_COUNT 5
+
+#define BU21013_SENSORS_BTN_0_7_REG 0x70
+#define BU21013_SENSORS_BTN_8_15_REG 0x71
+#define BU21013_SENSORS_BTN_16_23_REG 0x72
+#define BU21013_X1_POS_MSB_REG 0x73
+#define BU21013_X1_POS_LSB_REG 0x74
+#define BU21013_Y1_POS_MSB_REG 0x75
+#define BU21013_Y1_POS_LSB_REG 0x76
+#define BU21013_X2_POS_MSB_REG 0x77
+#define BU21013_X2_POS_LSB_REG 0x78
+#define BU21013_Y2_POS_MSB_REG 0x79
+#define BU21013_Y2_POS_LSB_REG 0x7A
+#define BU21013_INT_CLR_REG 0xE8
+#define BU21013_INT_MODE_REG 0xE9
+#define BU21013_GAIN_REG 0xEA
+#define BU21013_OFFSET_MODE_REG 0xEB
+#define BU21013_XY_EDGE_REG 0xEC
+#define BU21013_RESET_REG 0xED
+#define BU21013_CALIB_REG 0xEE
+#define BU21013_DONE_REG 0xEF
+#define BU21013_SENSOR_0_7_REG 0xF0
+#define BU21013_SENSOR_8_15_REG 0xF1
+#define BU21013_SENSOR_16_23_REG 0xF2
+#define BU21013_POS_MODE1_REG 0xF3
+#define BU21013_POS_MODE2_REG 0xF4
+#define BU21013_CLK_MODE_REG 0xF5
+#define BU21013_IDLE_REG 0xFA
+#define BU21013_FILTER_REG 0xFB
+#define BU21013_TH_ON_REG 0xFC
+#define BU21013_TH_OFF_REG 0xFD
+
+
+#define BU21013_RESET_ENABLE 0x01
+
+#define BU21013_SENSORS_EN_0_7 0x3F
+#define BU21013_SENSORS_EN_8_15 0xFC
+#define BU21013_SENSORS_EN_16_23 0x1F
+
+#define BU21013_POS_MODE1_0 0x02
+#define BU21013_POS_MODE1_1 0x04
+#define BU21013_POS_MODE1_2 0x08
+
+#define BU21013_POS_MODE2_ZERO 0x01
+#define BU21013_POS_MODE2_AVG1 0x02
+#define BU21013_POS_MODE2_AVG2 0x04
+#define BU21013_POS_MODE2_EN_XY 0x08
+#define BU21013_POS_MODE2_EN_RAW 0x10
+#define BU21013_POS_MODE2_MULTI 0x80
+
+#define BU21013_CLK_MODE_DIV 0x01
+#define BU21013_CLK_MODE_EXT 0x02
+#define BU21013_CLK_MODE_CALIB 0x80
+
+#define BU21013_IDLET_0 0x01
+#define BU21013_IDLET_1 0x02
+#define BU21013_IDLET_2 0x04
+#define BU21013_IDLET_3 0x08
+#define BU21013_IDLE_INTERMIT_EN 0x10
+
+#define BU21013_DELTA_0_6 0x7F
+#define BU21013_FILTER_EN 0x80
+
+#define BU21013_INT_MODE_LEVEL 0x00
+#define BU21013_INT_MODE_EDGE 0x01
+
+#define BU21013_GAIN_0 0x01
+#define BU21013_GAIN_1 0x02
+#define BU21013_GAIN_2 0x04
+
+#define BU21013_OFFSET_MODE_DEFAULT 0x00
+#define BU21013_OFFSET_MODE_MOVE 0x01
+#define BU21013_OFFSET_MODE_DISABLE 0x02
+
+#define BU21013_TH_ON_0 0x01
+#define BU21013_TH_ON_1 0x02
+#define BU21013_TH_ON_2 0x04
+#define BU21013_TH_ON_3 0x08
+#define BU21013_TH_ON_4 0x10
+#define BU21013_TH_ON_5 0x20
+#define BU21013_TH_ON_6 0x40
+#define BU21013_TH_ON_7 0x80
+#define BU21013_TH_ON_MAX 0xFF
+
+#define BU21013_TH_OFF_0 0x01
+#define BU21013_TH_OFF_1 0x02
+#define BU21013_TH_OFF_2 0x04
+#define BU21013_TH_OFF_3 0x08
+#define BU21013_TH_OFF_4 0x10
+#define BU21013_TH_OFF_5 0x20
+#define BU21013_TH_OFF_6 0x40
+#define BU21013_TH_OFF_7 0x80
+#define BU21013_TH_OFF_MAX 0xFF
+
+#define BU21013_X_EDGE_0 0x01
+#define BU21013_X_EDGE_1 0x02
+#define BU21013_X_EDGE_2 0x04
+#define BU21013_X_EDGE_3 0x08
+#define BU21013_Y_EDGE_0 0x10
+#define BU21013_Y_EDGE_1 0x20
+#define BU21013_Y_EDGE_2 0x40
+#define BU21013_Y_EDGE_3 0x80
+
+#define BU21013_DONE 0x01
+#define BU21013_NUMBER_OF_X_SENSORS (6)
+#define BU21013_NUMBER_OF_Y_SENSORS (11)
+
+#define DRIVER_TP "bu21013_tp"
+
+/**
+ * struct bu21013_ts_data - touch panel data structure
+ * @client: pointer to the i2c client
+ * @wait: variable to wait_queue_head_t structure
+ * @touch_stopped: touch stop flag
+ * @chip: pointer to the touch panel controller
+ * @in_dev: pointer to the input device structure
+ * @intr_pin: interrupt pin value
+ *
+ * Touch panel device data structure
+ */
+struct bu21013_ts_data {
+ struct i2c_client *client;
+ wait_queue_head_t wait;
+ bool touch_stopped;
+ const struct bu21013_platform_device *chip;
+ struct input_dev *in_dev;
+ unsigned int intr_pin;
+};
+
+/**
+ * bu21013_read_block_data(): read the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ * @buf: byte pointer
+ *
+ * Read the touch co-ordinates using i2c read block into buffer
+ * and returns integer.
+ */
+static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
+{
+ int ret, i;
+
+ for (i = 0; i < I2C_RETRY_COUNT; i++) {
+ ret = i2c_smbus_read_i2c_block_data
+ (data->client, BU21013_SENSORS_BTN_0_7_REG,
+ LENGTH_OF_BUFFER, buf);
+ if (ret == LENGTH_OF_BUFFER)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * bu21013_do_touch_report(): Get the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ *
+ * Get the touch co-ordinates from touch sensor registers and writes
+ * into device structure and returns integer.
+ */
+static int bu21013_do_touch_report(struct bu21013_ts_data *data)
+{
+ u8 buf[LENGTH_OF_BUFFER];
+ unsigned int pos_x[2], pos_y[2];
+ bool has_x_sensors, has_y_sensors;
+ int finger_down_count = 0;
+ int i;
+
+ if (data == NULL)
+ return -EINVAL;
+
+ if (bu21013_read_block_data(data, buf) < 0)
+ return -EINVAL;
+
+ has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
+ has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
+ ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
+ if (!has_x_sensors || !has_y_sensors)
+ return 0;
+
+ for (i = 0; i < MAX_FINGERS; i++) {
+ const u8 *p = &buf[4 * i + 3];
+ unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
+ unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
+ if (x == 0 || y == 0)
+ continue;
+ pos_x[finger_down_count] = x;
+ pos_y[finger_down_count] = y;
+ finger_down_count++;
+ }
+
+ if (finger_down_count) {
+ if (finger_down_count == 2 &&
+ (abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
+ abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) {
+ return 0;
+ }
+
+ for (i = 0; i < finger_down_count; i++) {
+ if (data->chip->x_flip)
+ pos_x[i] = data->chip->touch_x_max - pos_x[i];
+ if (data->chip->y_flip)
+ pos_y[i] = data->chip->touch_y_max - pos_y[i];
+
+ input_report_abs(data->in_dev,
+ ABS_MT_POSITION_X, pos_x[i]);
+ input_report_abs(data->in_dev,
+ ABS_MT_POSITION_Y, pos_y[i]);
+ input_mt_sync(data->in_dev);
+ }
+ } else
+ input_mt_sync(data->in_dev);
+
+ input_sync(data->in_dev);
+
+ return 0;
+}
+/**
+ * bu21013_gpio_irq() - gpio thread function for touch interrupt
+ * @irq: irq value
+ * @device_data: void pointer
+ *
+ * This gpio thread function for touch interrupt
+ * and returns irqreturn_t.
+ */
+static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
+{
+ struct bu21013_ts_data *data = device_data;
+ struct i2c_client *i2c = data->client;
+ int retval;
+
+ do {
+ retval = bu21013_do_touch_report(data);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
+ return IRQ_NONE;
+ }
+
+ data->intr_pin = data->chip->irq_read_val();
+ if (data->intr_pin == PEN_DOWN_INTR)
+ wait_event_timeout(data->wait, data->touch_stopped,
+ msecs_to_jiffies(2));
+ } while (!data->intr_pin && !data->touch_stopped);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * bu21013_init_chip() - power on sequence for the bu21013 controller
+ * @data: device structure pointer
+ *
+ * This function is used to power on
+ * the bu21013 controller and returns integer.
+ */
+static int bu21013_init_chip(struct bu21013_ts_data *data)
+{
+ int retval;
+ struct i2c_client *i2c = data->client;
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
+ BU21013_RESET_ENABLE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
+ return retval;
+ }
+ msleep(RESET_DELAY);
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
+ BU21013_SENSORS_EN_0_7);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
+ BU21013_SENSORS_EN_8_15);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
+ BU21013_SENSORS_EN_16_23);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
+ (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
+ (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
+ BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
+ BU21013_POS_MODE2_MULTI));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
+ return retval;
+ }
+
+ if (data->chip->ext_clk)
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
+ else
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
+ (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
+ BU21013_INT_MODE_LEVEL);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
+ (BU21013_DELTA_0_6 |
+ BU21013_FILTER_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
+ BU21013_TH_ON_5);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
+ BU21013_TH_OFF_4 || BU21013_TH_OFF_3);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
+ (BU21013_GAIN_0 | BU21013_GAIN_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
+ BU21013_OFFSET_MODE_DEFAULT);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
+ (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
+ BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
+ BU21013_DONE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+/**
+ * bu21013_free_irq() - frees IRQ registered for touchscreen
+ * @bu21013_data: device structure pointer
+ *
+ * This function signals interrupt thread to stop processing and
+ * frees interrupt.
+ */
+static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)
+{
+ bu21013_data->touch_stopped = true;
+ wake_up(&bu21013_data->wait);
+ free_irq(bu21013_data->chip->irq, bu21013_data);
+}
+
+/**
+ * bu21013_probe() - initializes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ * @id: i2c device id pointer
+ *
+ * This function used to initializes the i2c-client touchscreen
+ * driver and returns integer.
+ */
+static int __devinit bu21013_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bu21013_ts_data *bu21013_data;
+ struct input_dev *in_dev;
+ const struct bu21013_platform_device *pdata =
+ client->dev.platform_data;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "i2c smbus byte data not supported\n");
+ return -EIO;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data not defined\n");
+ return -EINVAL;
+ }
+
+ bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
+ in_dev = input_allocate_device();
+ if (!bu21013_data || !in_dev) {
+ dev_err(&client->dev, "device memory alloc failed\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ bu21013_data->in_dev = in_dev;
+ bu21013_data->chip = pdata;
+ bu21013_data->client = client;
+ bu21013_data->touch_stopped = false;
+ init_waitqueue_head(&bu21013_data->wait);
+
+ /* configure the gpio pins */
+ if (pdata->cs_en) {
+ error = pdata->cs_en(pdata->cs_pin);
+ if (error < 0) {
+ dev_err(&client->dev, "chip init failed\n");
+ goto err_free_mem;
+ }
+ }
+
+ /* configure the touch panel controller */
+ error = bu21013_init_chip(bu21013_data);
+ if (error) {
+ dev_err(&client->dev, "error in bu21013 config\n");
+ goto err_cs_disable;
+ }
+
+ /* register the device to input subsystem */
+ in_dev->name = DRIVER_TP;
+ in_dev->id.bustype = BUS_I2C;
+ in_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, in_dev->evbit);
+ __set_bit(EV_KEY, in_dev->evbit);
+ __set_bit(EV_ABS, in_dev->evbit);
+
+ input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max_res, 0, 0);
+ input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max_res, 0, 0);
+ input_set_drvdata(in_dev, bu21013_data);
+
+ error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq,
+ IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ DRIVER_TP, bu21013_data);
+ if (error) {
+ dev_err(&client->dev, "request irq %d failed\n", pdata->irq);
+ goto err_cs_disable;
+ }
+
+ error = input_register_device(in_dev);
+ if (error) {
+ dev_err(&client->dev, "failed to register input device\n");
+ goto err_free_irq;
+ }
+
+ device_init_wakeup(&client->dev, pdata->wakeup);
+ i2c_set_clientdata(client, bu21013_data);
+
+ return 0;
+
+err_free_irq:
+ bu21013_free_irq(bu21013_data);
+err_cs_disable:
+ pdata->cs_dis(pdata->cs_pin);
+err_free_mem:
+ input_free_device(bu21013_data->in_dev);
+ kfree(bu21013_data);
+
+ return error;
+}
+/**
+ * bu21013_remove() - removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This function uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int __devexit bu21013_remove(struct i2c_client *client)
+{
+ struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
+
+ bu21013_free_irq(bu21013_data);
+
+ bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin);
+
+ input_unregister_device(bu21013_data->in_dev);
+ kfree(bu21013_data);
+
+ device_init_wakeup(&client->dev, false);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * bu21013_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int bu21013_suspend(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+ struct i2c_client *client = bu21013_data->client;
+
+ bu21013_data->touch_stopped = true;
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(bu21013_data->chip->irq);
+ else
+ disable_irq(bu21013_data->chip->irq);
+
+ return 0;
+}
+
+/**
+ * bu21013_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int bu21013_resume(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+ struct i2c_client *client = bu21013_data->client;
+ int retval;
+
+ retval = bu21013_init_chip(bu21013_data);
+ if (retval < 0) {
+ dev_err(&client->dev, "bu21013 controller config failed\n");
+ return retval;
+ }
+
+ bu21013_data->touch_stopped = false;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(bu21013_data->chip->irq);
+ else
+ enable_irq(bu21013_data->chip->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops bu21013_dev_pm_ops = {
+ .suspend = bu21013_suspend,
+ .resume = bu21013_resume,
+};
+#endif
+
+static const struct i2c_device_id bu21013_id[] = {
+ { DRIVER_TP, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bu21013_id);
+
+static struct i2c_driver bu21013_driver = {
+ .driver = {
+ .name = DRIVER_TP,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &bu21013_dev_pm_ops,
+#endif
+ },
+ .probe = bu21013_probe,
+ .remove = __devexit_p(bu21013_remove),
+ .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_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
+MODULE_DESCRIPTION("bu21013 touch screen controller driver");
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
index 5ec0946938fe..d0c3a7229adf 100644
--- a/drivers/input/touchscreen/cy8ctmg110_ts.c
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -206,9 +206,9 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client,
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X,
- CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0);
+ CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
input_set_abs_params(input_dev, ABS_Y,
- CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0);
+ CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
if (ts->reset_pin) {
err = gpio_request(ts->reset_pin, NULL);
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
index a89700e7ace4..498bd62af09a 100644
--- a/drivers/input/touchscreen/hp680_ts_input.c
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -107,8 +107,7 @@ static int __init hp680_ts_init(void)
return 0;
fail2: free_irq(HP680_TS_IRQ, NULL);
- cancel_delayed_work(&work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&work);
fail1: input_free_device(hp680_ts_dev);
return err;
}
@@ -116,8 +115,7 @@ static int __init hp680_ts_init(void)
static void __exit hp680_ts_exit(void)
{
free_irq(HP680_TS_IRQ, NULL);
- cancel_delayed_work(&work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&work);
input_unregister_device(hp680_ts_dev);
}
diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c
new file mode 100644
index 000000000000..c0307b22d86f
--- /dev/null
+++ b/drivers/input/touchscreen/intel-mid-touch.c
@@ -0,0 +1,687 @@
+/*
+ * Intel MID Resistive Touch Screen Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
+ * Ramesh Agarwal (ramesh.agarwal@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * TODO:
+ * review conversion of r/m/w sequences
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/intel_scu_ipc.h>
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_INT 0x04 /* PMIC interrupt register */
+#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */
+
+/* ADC Interrupt registers */
+#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */
+#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */
+
+/* ADC Control registers */
+#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */
+
+/* ADC Channel Selection registers */
+#define PMICADDR0 0xA4
+#define END_OF_CHANNEL 0x1F
+
+/* ADC Result register */
+#define PMIC_REG_ADCSNS0H 0x64
+
+/* ADC channels for touch screen */
+#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
+#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
+#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
+#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
+
+/* Touch screen channel BIAS constants */
+#define MRST_XBIAS 0x20
+#define MRST_YBIAS 0x40
+#define MRST_ZBIAS 0x80
+
+/* Touch screen coordinates */
+#define MRST_X_MIN 10
+#define MRST_X_MAX 1024
+#define MRST_X_FUZZ 5
+#define MRST_Y_MIN 10
+#define MRST_Y_MAX 1024
+#define MRST_Y_FUZZ 5
+#define MRST_PRESSURE_MIN 0
+#define MRST_PRESSURE_NOMINAL 50
+#define MRST_PRESSURE_MAX 100
+
+#define WAIT_ADC_COMPLETION 10 /* msec */
+
+/* PMIC ADC round robin delays */
+#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
+#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
+
+/* PMIC Vendor Identifiers */
+#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
+#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
+#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
+#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
+
+/* Touch screen device structure */
+struct mrstouch_dev {
+ struct device *dev; /* device associated with touch screen */
+ struct input_dev *input;
+ char phys[32];
+ u16 asr; /* Address selection register */
+ int irq;
+ unsigned int vendor; /* PMIC vendor */
+ unsigned int rev; /* PMIC revision */
+
+ int (*read_prepare)(struct mrstouch_dev *tsdev);
+ int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);
+ int (*read_finish)(struct mrstouch_dev *tsdev);
+};
+
+
+/*************************** NEC and Maxim Interface ************************/
+
+static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+ return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);
+}
+
+/*
+ * Enables PENDET interrupt.
+ */
+static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+ int err;
+
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);
+ if (!err)
+ err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);
+
+ return err;
+}
+
+/*
+ * Reads PMIC ADC touch screen result
+ * Reads ADC storage registers for higher 7 and lower 3 bits and
+ * converts the two readings into a single value and turns off gain bit
+ */
+static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
+{
+ int err;
+ u16 result;
+ u32 res;
+
+ result = PMIC_REG_ADCSNS0H + offset;
+
+ if (chan == MRST_TS_CHAN12)
+ result += 4;
+
+ err = intel_scu_ipc_ioread32(result, &res);
+ if (err)
+ return err;
+
+ /* Mash the bits up */
+
+ *vp = (res & 0xFF) << 3; /* Highest 7 bits */
+ *vp |= (res >> 8) & 0x07; /* Lower 3 bits */
+ *vp &= 0x3FF;
+
+ res >>= 16;
+
+ *vm = (res & 0xFF) << 3; /* Highest 7 bits */
+ *vm |= (res >> 8) & 0x07; /* Lower 3 bits */
+ *vm &= 0x3FF;
+
+ return 0;
+}
+
+/*
+ * Enables X, Y and Z bias values
+ * Enables YPYM for X channels and XPXM for Y channels
+ */
+static int mrstouch_ts_bias_set(uint offset, uint bias)
+{
+ int count;
+ u16 chan, start;
+ u16 reg[4];
+ u8 data[4];
+
+ chan = PMICADDR0 + offset;
+ start = MRST_TS_CHAN10;
+
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = bias | (start + count);
+ }
+
+ return intel_scu_ipc_writev(reg, data, 4);
+}
+
+/* To read touch screen channel values */
+static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,
+ u16 *x, u16 *y, u16 *z)
+{
+ int err;
+ u16 xm, ym, zm;
+
+ /* configure Y bias for X channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read x+ and x- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);
+ if (err)
+ goto ipc_error;
+
+ /* configure x bias for y channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read y+ and y- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);
+ if (err)
+ goto ipc_error;
+
+ /* configure z bias for x and y channels */
+ err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* read z+ and z- channels */
+ err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);
+ if (err)
+ goto ipc_error;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during adc read\n");
+ return err;
+}
+
+
+/*************************** Freescale Interface ************************/
+
+static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+ int err, count;
+ u16 chan;
+ u16 reg[5];
+ u8 data[5];
+
+ /* Stop the ADC */
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
+ if (err)
+ goto ipc_error;
+
+ chan = PMICADDR0 + tsdev->asr;
+
+ /* Set X BIAS */
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = 0x2A;
+ }
+ reg[count] = chan++; /* Dummy */
+ data[count] = 0;
+
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* Set Y BIAS */
+ for (count = 0; count <= 3; count++) {
+ reg[count] = chan++;
+ data[count] = 0x4A;
+ }
+ reg[count] = chan++; /* Dummy */
+ data[count] = 0;
+
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ /* Set Z BIAS */
+ err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
+ if (err)
+ goto ipc_error;
+
+ msleep(WAIT_ADC_COMPLETION);
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,
+ u16 *x, u16 *y, u16 *z)
+{
+ int err;
+ u16 result;
+ u16 reg[4];
+ u8 data[4];
+
+ result = PMIC_REG_ADCSNS0H + tsdev->asr;
+
+ reg[0] = result + 4;
+ reg[1] = result + 5;
+ reg[2] = result + 16;
+ reg[3] = result + 17;
+
+ err = intel_scu_ipc_readv(reg, data, 4);
+ if (err)
+ goto ipc_error;
+
+ *x = data[0] << 3; /* Higher 7 bits */
+ *x |= data[1] & 0x7; /* Lower 3 bits */
+ *x &= 0x3FF;
+
+ *y = data[2] << 3; /* Higher 7 bits */
+ *y |= data[3] & 0x7; /* Lower 3 bits */
+ *y &= 0x3FF;
+
+ /* Read Z value */
+ reg[0] = result + 28;
+ reg[1] = result + 29;
+
+ err = intel_scu_ipc_readv(reg, data, 4);
+ if (err)
+ goto ipc_error;
+
+ *z = data[0] << 3; /* Higher 7 bits */
+ *z |= data[1] & 0x7; /* Lower 3 bits */
+ *z &= 0x3FF;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+ int err, count;
+ u16 chan;
+ u16 reg[5];
+ u8 data[5];
+
+ /* Clear all TS channels */
+ chan = PMICADDR0 + tsdev->asr;
+ for (count = 0; count <= 4; count++) {
+ reg[count] = chan++;
+ data[count] = 0;
+ }
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ for (count = 0; count <= 4; count++) {
+ reg[count] = chan++;
+ data[count] = 0;
+ }
+ err = intel_scu_ipc_writev(reg, data, 5);
+ if (err)
+ goto ipc_error;
+
+ err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
+ if (err)
+ goto ipc_error;
+
+ /* Start ADC */
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
+ if (err)
+ goto ipc_error;
+
+ return 0;
+
+ipc_error:
+ dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+ return err;
+}
+
+static void mrstouch_report_event(struct input_dev *input,
+ unsigned int x, unsigned int y, unsigned int z)
+{
+ if (z > MRST_PRESSURE_NOMINAL) {
+ /* Pen touched, report button touch and coordinates */
+ input_report_key(input, BTN_TOUCH, 1);
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ } else {
+ input_report_key(input, BTN_TOUCH, 0);
+ }
+
+ input_report_abs(input, ABS_PRESSURE, z);
+ input_sync(input);
+}
+
+/* PENDET interrupt handler */
+static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)
+{
+ struct mrstouch_dev *tsdev = dev_id;
+ u16 x, y, z;
+
+ /*
+ * Should we lower thread priority? Probably not, since we are
+ * not spinning but sleeping...
+ */
+
+ if (tsdev->read_prepare(tsdev))
+ goto out;
+
+ do {
+ if (tsdev->read(tsdev, &x, &y, &z))
+ break;
+
+ mrstouch_report_event(tsdev->input, x, y, z);
+ } while (z > MRST_PRESSURE_NOMINAL);
+
+ tsdev->read_finish(tsdev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+/* Utility to read PMIC ID */
+static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)
+{
+ int err;
+ u8 r;
+
+ err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
+ if (err)
+ return err;
+
+ *vendor = r & 0x7;
+ *rev = (r >> 3) & 0x7;
+
+ return 0;
+}
+
+/*
+ * Parse ADC channels to find end of the channel configured by other ADC user
+ * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
+ */
+static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
+{
+ int err, i, found;
+ u8 r8;
+
+ found = -1;
+
+ for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
+ if (found >= 0)
+ break;
+
+ err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
+ if (err)
+ return err;
+
+ if (r8 == END_OF_CHANNEL) {
+ found = i;
+ break;
+ }
+ }
+ if (found < 0)
+ return 0;
+
+ if (tsdev->vendor == PMIC_VENDOR_FS) {
+ if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
+ return -ENOSPC;
+ } else {
+ if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
+ return -ENOSPC;
+ }
+ return found;
+}
+
+
+/*
+ * Writes touch screen channels to ADC address selection registers
+ */
+static int __devinit mrstouch_ts_chan_set(uint offset)
+{
+ u16 chan;
+
+ int ret, count;
+
+ chan = PMICADDR0 + offset;
+ for (count = 0; count <= 3; count++) {
+ ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);
+ if (ret)
+ return ret;
+ }
+ return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);
+}
+
+/* Initialize ADC */
+static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)
+{
+ int err, start;
+ u8 ra, rm;
+
+ err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);
+ if (err) {
+ dev_err(tsdev->dev, "Unable to read PMIC id\n");
+ return err;
+ }
+
+ switch (tsdev->vendor) {
+ case PMIC_VENDOR_NEC:
+ case PMIC_VENDOR_MAXIM:
+ tsdev->read_prepare = mrstouch_nec_adc_read_prepare;
+ tsdev->read = mrstouch_nec_adc_read;
+ tsdev->read_finish = mrstouch_nec_adc_read_finish;
+ break;
+
+ case PMIC_VENDOR_FS:
+ tsdev->read_prepare = mrstouch_fs_adc_read_prepare;
+ tsdev->read = mrstouch_fs_adc_read;
+ tsdev->read_finish = mrstouch_fs_adc_read_finish;
+ break;
+
+ default:
+ dev_err(tsdev->dev,
+ "Unsupported touchscreen: %d\n", tsdev->vendor);
+ return -ENXIO;
+ }
+
+ start = mrstouch_chan_parse(tsdev);
+ if (start < 0) {
+ dev_err(tsdev->dev, "Unable to parse channels\n");
+ return start;
+ }
+
+ tsdev->asr = start;
+
+ /*
+ * ADC power on, start, enable PENDET and set loop delay
+ * ADC loop delay is set to 4.5 ms approximately
+ * Loop delay more than this results in jitter in adc readings
+ * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET
+ * interrupt generation sometimes.
+ */
+
+ if (tsdev->vendor == PMIC_VENDOR_FS) {
+ ra = 0xE0 | ADC_LOOP_DELAY0;
+ rm = 0x5;
+ } else {
+ /* NEC and MAXIm not consistent with loop delay 0 */
+ ra = 0xE0 | ADC_LOOP_DELAY1;
+ rm = 0x0;
+
+ /* configure touch screen channels */
+ err = mrstouch_ts_chan_set(tsdev->asr);
+ if (err)
+ return err;
+ }
+
+ err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
+ if (err)
+ return err;
+
+ err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+/* Probe function for touch screen driver */
+static int __devinit mrstouch_probe(struct platform_device *pdev)
+{
+ struct mrstouch_dev *tsdev;
+ struct input_dev *input;
+ int err;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no interrupt assigned\n");
+ return -EINVAL;
+ }
+
+ tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!tsdev || !input) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsdev->dev = &pdev->dev;
+ tsdev->input = input;
+ tsdev->irq = irq;
+
+ snprintf(tsdev->phys, sizeof(tsdev->phys),
+ "%s/input0", dev_name(tsdev->dev));
+
+ err = mrstouch_adc_init(tsdev);
+ if (err) {
+ dev_err(&pdev->dev, "ADC initialization failed\n");
+ goto err_free_mem;
+ }
+
+ input->name = "mrst_touchscreen";
+ input->phys = tsdev->phys;
+ input->dev.parent = tsdev->dev;
+
+ input->id.vendor = tsdev->vendor;
+ input->id.version = tsdev->rev;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(tsdev->input, ABS_X,
+ MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);
+ input_set_abs_params(tsdev->input, ABS_Y,
+ MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);
+ input_set_abs_params(tsdev->input, ABS_PRESSURE,
+ MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);
+
+ err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq,
+ 0, "mrstouch", tsdev);
+ if (err) {
+ dev_err(tsdev->dev, "unable to allocate irq\n");
+ goto err_free_mem;
+ }
+
+ err = input_register_device(tsdev->input);
+ if (err) {
+ dev_err(tsdev->dev, "unable to register input device\n");
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, tsdev);
+ return 0;
+
+err_free_irq:
+ free_irq(tsdev->irq, tsdev);
+err_free_mem:
+ input_free_device(input);
+ kfree(tsdev);
+ return err;
+}
+
+static int __devexit mrstouch_remove(struct platform_device *pdev)
+{
+ struct mrstouch_dev *tsdev = platform_get_drvdata(pdev);
+
+ free_irq(tsdev->irq, tsdev);
+ input_unregister_device(tsdev->input);
+ kfree(tsdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mrstouch_driver = {
+ .driver = {
+ .name = "pmic_touch",
+ .owner = THIS_MODULE,
+ },
+ .probe = mrstouch_probe,
+ .remove = __devexit_p(mrstouch_remove),
+};
+
+static int __init mrstouch_init(void)
+{
+ return platform_driver_register(&mrstouch_driver);
+}
+module_init(mrstouch_init);
+
+static void __exit mrstouch_exit(void)
+{
+ platform_driver_unregister(&mrstouch_driver);
+}
+module_exit(mrstouch_exit);
+
+MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
+MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c
new file mode 100644
index 000000000000..dcf803f5a1f7
--- /dev/null
+++ b/drivers/input/touchscreen/lpc32xx_ts.c
@@ -0,0 +1,411 @@
+/*
+ * LPC32xx built-in touchscreen driver
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/*
+ * Touchscreen controller register offsets
+ */
+#define LPC32XX_TSC_STAT 0x00
+#define LPC32XX_TSC_SEL 0x04
+#define LPC32XX_TSC_CON 0x08
+#define LPC32XX_TSC_FIFO 0x0C
+#define LPC32XX_TSC_DTR 0x10
+#define LPC32XX_TSC_RTR 0x14
+#define LPC32XX_TSC_UTR 0x18
+#define LPC32XX_TSC_TTR 0x1C
+#define LPC32XX_TSC_DXP 0x20
+#define LPC32XX_TSC_MIN_X 0x24
+#define LPC32XX_TSC_MAX_X 0x28
+#define LPC32XX_TSC_MIN_Y 0x2C
+#define LPC32XX_TSC_MAX_Y 0x30
+#define LPC32XX_TSC_AUX_UTR 0x34
+#define LPC32XX_TSC_AUX_MIN 0x38
+#define LPC32XX_TSC_AUX_MAX 0x3C
+
+#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8)
+#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7)
+
+#define LPC32XX_TSC_SEL_DEFVAL 0x0284
+
+#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11)
+#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7)
+#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4)
+#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2)
+#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0)
+
+#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31)
+#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16)
+#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF)
+
+#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF
+
+#define LPC32XX_TSC_MIN_XY_VAL 0x0
+#define LPC32XX_TSC_MAX_XY_VAL 0x3FF
+
+#define MOD_NAME "ts-lpc32xx"
+
+#define tsc_readl(dev, reg) \
+ __raw_readl((dev)->tsc_base + (reg))
+#define tsc_writel(dev, reg, val) \
+ __raw_writel((val), (dev)->tsc_base + (reg))
+
+struct lpc32xx_tsc {
+ struct input_dev *dev;
+ void __iomem *tsc_base;
+ int irq;
+ struct clk *clk;
+};
+
+static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc)
+{
+ while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+ LPC32XX_TSC_STAT_FIFO_EMPTY))
+ tsc_readl(tsc, LPC32XX_TSC_FIFO);
+}
+
+static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id)
+{
+ u32 tmp, rv[4], xs[4], ys[4];
+ int idx;
+ struct lpc32xx_tsc *tsc = dev_id;
+ struct input_dev *input = tsc->dev;
+
+ tmp = tsc_readl(tsc, LPC32XX_TSC_STAT);
+
+ if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
+ /* FIFO overflow - throw away samples */
+ lpc32xx_fifo_clear(tsc);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Gather and normalize 4 samples. Pen-up events may have less
+ * than 4 samples, but its ok to pop 4 and let the last sample
+ * pen status check drop the samples.
+ */
+ idx = 0;
+ while (idx < 4 &&
+ !(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+ LPC32XX_TSC_STAT_FIFO_EMPTY)) {
+ tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO);
+ xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+ LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
+ ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+ LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
+ rv[idx] = tmp;
+ idx++;
+ }
+
+ /* Data is only valid if pen is still down in last sample */
+ if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) {
+ /* Use average of 2nd and 3rd sample for position */
+ input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
+ input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
+ input_report_key(input, BTN_TOUCH, 1);
+ } else {
+ input_report_key(input, BTN_TOUCH, 0);
+ }
+
+ input_sync(input);
+
+ return IRQ_HANDLED;
+}
+
+static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
+{
+ /* Disable auto mode */
+ tsc_writel(tsc, LPC32XX_TSC_CON,
+ tsc_readl(tsc, LPC32XX_TSC_CON) &
+ ~LPC32XX_TSC_ADCCON_AUTO_EN);
+
+ clk_disable(tsc->clk);
+}
+
+static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
+{
+ u32 tmp;
+
+ clk_enable(tsc->clk);
+
+ tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
+
+ /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
+ tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
+ LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
+ LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
+ tsc_writel(tsc, LPC32XX_TSC_CON, tmp);
+
+ /* These values are all preset */
+ tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
+ tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
+ tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
+
+ /* Aux support is not used */
+ tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0);
+ tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0);
+ tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0);
+
+ /*
+ * Set sample rate to about 240Hz per X/Y pair. A single measurement
+ * consists of 4 pairs which gives about a 60Hz sample rate based on
+ * a stable 32768Hz clock source. Values are in clocks.
+ * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
+ */
+ tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2);
+ tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2);
+ tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10);
+ tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4);
+ tsc_writel(tsc, LPC32XX_TSC_UTR, 88);
+
+ lpc32xx_fifo_clear(tsc);
+
+ /* Enable automatic ts event capture */
+ tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
+}
+
+static int lpc32xx_ts_open(struct input_dev *dev)
+{
+ struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+ lpc32xx_setup_tsc(tsc);
+
+ return 0;
+}
+
+static void lpc32xx_ts_close(struct input_dev *dev)
+{
+ struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+ lpc32xx_stop_tsc(tsc);
+}
+
+static int __devinit lpc32xx_ts_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_tsc *tsc;
+ struct input_dev *input;
+ struct resource *res;
+ resource_size_t size;
+ int irq;
+ int error;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Can't get memory resource\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Can't get interrupt resource\n");
+ return irq;
+ }
+
+ tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!tsc || !input) {
+ dev_err(&pdev->dev, "failed allocating memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ tsc->dev = input;
+ tsc->irq = irq;
+
+ size = resource_size(res);
+
+ if (!request_mem_region(res->start, size, pdev->name)) {
+ dev_err(&pdev->dev, "TSC registers are not free\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ tsc->tsc_base = ioremap(res->start, size);
+ if (!tsc->tsc_base) {
+ dev_err(&pdev->dev, "Can't map memory\n");
+ error = -ENOMEM;
+ goto err_release_mem;
+ }
+
+ tsc->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tsc->clk)) {
+ dev_err(&pdev->dev, "failed getting clock\n");
+ error = PTR_ERR(tsc->clk);
+ goto err_unmap;
+ }
+
+ input->name = MOD_NAME;
+ input->phys = "lpc32xx/input0";
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0002;
+ input->id.version = 0x0100;
+ input->dev.parent = &pdev->dev;
+ input->open = lpc32xx_ts_open;
+ input->close = lpc32xx_ts_close;
+
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,
+ LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+ input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,
+ LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+
+ input_set_drvdata(input, tsc);
+
+ error = request_irq(tsc->irq, lpc32xx_ts_interrupt,
+ IRQF_DISABLED, pdev->name, tsc);
+ if (error) {
+ dev_err(&pdev->dev, "failed requesting interrupt\n");
+ goto err_put_clock;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "failed registering input device\n");
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, tsc);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+err_free_irq:
+ free_irq(tsc->irq, tsc);
+err_put_clock:
+ clk_put(tsc->clk);
+err_unmap:
+ iounmap(tsc->tsc_base);
+err_release_mem:
+ release_mem_region(res->start, size);
+err_free_mem:
+ input_free_device(input);
+ kfree(tsc);
+
+ return error;
+}
+
+static int __devexit lpc32xx_ts_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ device_init_wakeup(&pdev->dev, 0);
+ free_irq(tsc->irq, tsc);
+
+ input_unregister_device(tsc->dev);
+
+ clk_put(tsc->clk);
+
+ iounmap(tsc->tsc_base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(tsc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_ts_suspend(struct device *dev)
+{
+ struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+ struct input_dev *input = tsc->dev;
+
+ /*
+ * Suspend and resume can be called when the device hasn't been
+ * enabled. If there are no users that have the device open, then
+ * avoid calling the TSC stop and start functions as the TSC
+ * isn't yet clocked.
+ */
+ mutex_lock(&input->mutex);
+
+ if (input->users) {
+ if (device_may_wakeup(dev))
+ enable_irq_wake(tsc->irq);
+ else
+ lpc32xx_stop_tsc(tsc);
+ }
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static int lpc32xx_ts_resume(struct device *dev)
+{
+ struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+ struct input_dev *input = tsc->dev;
+
+ mutex_lock(&input->mutex);
+
+ if (input->users) {
+ if (device_may_wakeup(dev))
+ disable_irq_wake(tsc->irq);
+ else
+ lpc32xx_setup_tsc(tsc);
+ }
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops lpc32xx_ts_pm_ops = {
+ .suspend = lpc32xx_ts_suspend,
+ .resume = lpc32xx_ts_resume,
+};
+#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops)
+#else
+#define LPC32XX_TS_PM_OPS NULL
+#endif
+
+static struct platform_driver lpc32xx_ts_driver = {
+ .probe = lpc32xx_ts_probe,
+ .remove = __devexit_p(lpc32xx_ts_remove),
+ .driver = {
+ .name = MOD_NAME,
+ .owner = THIS_MODULE,
+ .pm = LPC32XX_TS_PM_OPS,
+ },
+};
+
+static int __init lpc32xx_ts_init(void)
+{
+ return platform_driver_register(&lpc32xx_ts_driver);
+}
+module_init(lpc32xx_ts_init);
+
+static void __exit lpc32xx_ts_exit(void)
+{
+ platform_driver_unregister(&lpc32xx_ts_driver);
+}
+module_exit(lpc32xx_ts_exit);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");
+MODULE_DESCRIPTION("LPC32XX TSC Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lpc32xx_ts");
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index 6085d12fd561..8feb7f3c8be1 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -350,7 +350,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev)
err_tcirq:
free_irq(ts.irq_tc, ts.input);
err_inputdev:
- input_unregister_device(ts.input);
+ input_free_device(ts.input);
err_iomap:
iounmap(ts.io);
err_clk:
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
index 656148ec0027..ae88e13c99ff 100644
--- a/drivers/input/touchscreen/stmpe-ts.c
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -268,7 +268,7 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)
struct stmpe_touch *ts;
struct input_dev *idev;
struct stmpe_ts_platform_data *ts_pdata = NULL;
- int ret = 0;
+ int ret;
int ts_irq;
ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
@@ -276,12 +276,16 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)
return ts_irq;
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
- if (!ts)
+ if (!ts) {
+ ret = -ENOMEM;
goto err_out;
+ }
idev = input_allocate_device();
- if (!idev)
+ if (!idev) {
+ ret = -ENOMEM;
goto err_free_ts;
+ }
platform_set_drvdata(pdev, ts);
ts->stmpe = stmpe;
@@ -361,7 +365,6 @@ static int __devexit stmpe_ts_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
input_unregister_device(ts->idev);
- input_free_device(ts->idev);
kfree(ts);
diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c
new file mode 100644
index 000000000000..cf1dba2e267c
--- /dev/null
+++ b/drivers/input/touchscreen/tnetv107x-ts.c
@@ -0,0 +1,396 @@
+/*
+ * Texas Instruments TNETV107X Touchscreen Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation 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/errno.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/tnetv107x.h>
+
+#define TSC_PENUP_POLL (HZ / 5)
+#define IDLE_TIMEOUT 100 /* msec */
+
+/*
+ * The first and last samples of a touch interval are usually garbage and need
+ * to be filtered out with these devices. The following definitions control
+ * the number of samples skipped.
+ */
+#define TSC_HEAD_SKIP 1
+#define TSC_TAIL_SKIP 1
+#define TSC_SKIP (TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1)
+#define TSC_SAMPLES (TSC_SKIP + 1)
+
+/* Register Offsets */
+struct tsc_regs {
+ u32 rev;
+ u32 tscm;
+ u32 bwcm;
+ u32 swc;
+ u32 adcchnl;
+ u32 adcdata;
+ u32 chval[4];
+};
+
+/* TSC Mode Configuration Register (tscm) bits */
+#define WMODE BIT(0)
+#define TSKIND BIT(1)
+#define ZMEASURE_EN BIT(2)
+#define IDLE BIT(3)
+#define TSC_EN BIT(4)
+#define STOP BIT(5)
+#define ONE_SHOT BIT(6)
+#define SINGLE BIT(7)
+#define AVG BIT(8)
+#define AVGNUM(x) (((x) & 0x03) << 9)
+#define PVSTC(x) (((x) & 0x07) << 11)
+#define PON BIT(14)
+#define PONBG BIT(15)
+#define AFERST BIT(16)
+
+/* ADC DATA Capture Register bits */
+#define DATA_VALID BIT(16)
+
+/* Register Access Macros */
+#define tsc_read(ts, reg) __raw_readl(&(ts)->regs->reg)
+#define tsc_write(ts, reg, val) __raw_writel(val, &(ts)->regs->reg);
+#define tsc_set_bits(ts, reg, val) \
+ tsc_write(ts, reg, tsc_read(ts, reg) | (val))
+#define tsc_clr_bits(ts, reg, val) \
+ tsc_write(ts, reg, tsc_read(ts, reg) & ~(val))
+
+struct sample {
+ int x, y, p;
+};
+
+struct tsc_data {
+ struct input_dev *input_dev;
+ struct resource *res;
+ struct tsc_regs __iomem *regs;
+ struct timer_list timer;
+ spinlock_t lock;
+ struct clk *clk;
+ struct device *dev;
+ int sample_count;
+ struct sample samples[TSC_SAMPLES];
+ int tsc_irq;
+};
+
+static int tsc_read_sample(struct tsc_data *ts, struct sample* sample)
+{
+ int x, y, z1, z2, t, p = 0;
+ u32 val;
+
+ val = tsc_read(ts, chval[0]);
+ if (val & DATA_VALID)
+ x = val & 0xffff;
+ else
+ return -EINVAL;
+
+ y = tsc_read(ts, chval[1]) & 0xffff;
+ z1 = tsc_read(ts, chval[2]) & 0xffff;
+ z2 = tsc_read(ts, chval[3]) & 0xffff;
+
+ if (z1) {
+ t = ((600 * x) * (z2 - z1));
+ p = t / (u32) (z1 << 12);
+ if (p < 0)
+ p = 0;
+ }
+
+ sample->x = x;
+ sample->y = y;
+ sample->p = p;
+
+ return 0;
+}
+
+static void tsc_poll(unsigned long data)
+{
+ struct tsc_data *ts = (struct tsc_data *)data;
+ unsigned long flags;
+ int i, val, x, y, p;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ if (ts->sample_count >= TSC_SKIP) {
+ input_report_abs(ts->input_dev, ABS_PRESSURE, 0);
+ input_report_key(ts->input_dev, BTN_TOUCH, 0);
+ input_sync(ts->input_dev);
+ } else if (ts->sample_count > 0) {
+ /*
+ * A touch event lasted less than our skip count. Salvage and
+ * report anyway.
+ */
+ for (i = 0, val = 0; i < ts->sample_count; i++)
+ val += ts->samples[i].x;
+ x = val / ts->sample_count;
+
+ for (i = 0, val = 0; i < ts->sample_count; i++)
+ val += ts->samples[i].y;
+ y = val / ts->sample_count;
+
+ for (i = 0, val = 0; i < ts->sample_count; i++)
+ val += ts->samples[i].p;
+ p = val / ts->sample_count;
+
+ input_report_abs(ts->input_dev, ABS_X, x);
+ input_report_abs(ts->input_dev, ABS_Y, y);
+ input_report_abs(ts->input_dev, ABS_PRESSURE, p);
+ input_report_key(ts->input_dev, BTN_TOUCH, 1);
+ input_sync(ts->input_dev);
+ }
+
+ ts->sample_count = 0;
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static irqreturn_t tsc_irq(int irq, void *dev_id)
+{
+ struct tsc_data *ts = (struct tsc_data *)dev_id;
+ struct sample *sample;
+ int index;
+
+ spin_lock(&ts->lock);
+
+ index = ts->sample_count % TSC_SAMPLES;
+ sample = &ts->samples[index];
+ if (tsc_read_sample(ts, sample) < 0)
+ goto out;
+
+ if (++ts->sample_count >= TSC_SKIP) {
+ index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES;
+ sample = &ts->samples[index];
+
+ input_report_abs(ts->input_dev, ABS_X, sample->x);
+ input_report_abs(ts->input_dev, ABS_Y, sample->y);
+ input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p);
+ if (ts->sample_count == TSC_SKIP)
+ input_report_key(ts->input_dev, BTN_TOUCH, 1);
+ input_sync(ts->input_dev);
+ }
+ mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL);
+out:
+ spin_unlock(&ts->lock);
+ return IRQ_HANDLED;
+}
+
+static int tsc_start(struct input_dev *dev)
+{
+ struct tsc_data *ts = input_get_drvdata(dev);
+ unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT);
+ u32 val;
+
+ clk_enable(ts->clk);
+
+ /* Go to idle mode, before any initialization */
+ while (time_after(timeout, jiffies)) {
+ if (tsc_read(ts, tscm) & IDLE)
+ break;
+ }
+
+ if (time_before(timeout, jiffies)) {
+ dev_warn(ts->dev, "timeout waiting for idle\n");
+ clk_disable(ts->clk);
+ return -EIO;
+ }
+
+ /* Configure TSC Control register*/
+ val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN);
+ tsc_write(ts, tscm, val);
+
+ /* Bring TSC out of reset: Clear AFE reset bit */
+ val &= ~(AFERST);
+ tsc_write(ts, tscm, val);
+
+ /* Configure all pins for hardware control*/
+ tsc_write(ts, bwcm, 0);
+
+ /* Finally enable the TSC */
+ tsc_set_bits(ts, tscm, TSC_EN);
+
+ return 0;
+}
+
+static void tsc_stop(struct input_dev *dev)
+{
+ struct tsc_data *ts = input_get_drvdata(dev);
+
+ tsc_clr_bits(ts, tscm, TSC_EN);
+ synchronize_irq(ts->tsc_irq);
+ del_timer_sync(&ts->timer);
+ clk_disable(ts->clk);
+}
+
+static int __devinit tsc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tsc_data *ts;
+ int error = 0;
+ u32 rev = 0;
+
+ ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL);
+ if (!ts) {
+ dev_err(dev, "cannot allocate device info\n");
+ return -ENOMEM;
+ }
+
+ ts->dev = dev;
+ spin_lock_init(&ts->lock);
+ setup_timer(&ts->timer, tsc_poll, (unsigned long)ts);
+ platform_set_drvdata(pdev, ts);
+
+ ts->tsc_irq = platform_get_irq(pdev, 0);
+ if (ts->tsc_irq < 0) {
+ dev_err(dev, "cannot determine device interrupt\n");
+ error = -ENODEV;
+ goto error_res;
+ }
+
+ ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ts->res) {
+ dev_err(dev, "cannot determine register area\n");
+ error = -ENODEV;
+ goto error_res;
+ }
+
+ if (!request_mem_region(ts->res->start, resource_size(ts->res),
+ pdev->name)) {
+ dev_err(dev, "cannot claim register memory\n");
+ ts->res = NULL;
+ error = -EINVAL;
+ goto error_res;
+ }
+
+ ts->regs = ioremap(ts->res->start, resource_size(ts->res));
+ if (!ts->regs) {
+ dev_err(dev, "cannot map register memory\n");
+ error = -ENOMEM;
+ goto error_map;
+ }
+
+ ts->clk = clk_get(dev, NULL);
+ if (!ts->clk) {
+ dev_err(dev, "cannot claim device clock\n");
+ error = -EINVAL;
+ goto error_clk;
+ }
+
+ error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, 0,
+ dev_name(dev), ts);
+ if (error < 0) {
+ dev_err(ts->dev, "Could not allocate ts irq\n");
+ goto error_irq;
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (!ts->input_dev) {
+ dev_err(dev, "cannot allocate input device\n");
+ error = -ENOMEM;
+ goto error_input;
+ }
+ input_set_drvdata(ts->input_dev, ts);
+
+ ts->input_dev->name = pdev->name;
+ ts->input_dev->id.bustype = BUS_HOST;
+ ts->input_dev->dev.parent = &pdev->dev;
+ ts->input_dev->open = tsc_start;
+ ts->input_dev->close = tsc_stop;
+
+ clk_enable(ts->clk);
+ rev = tsc_read(ts, rev);
+ ts->input_dev->id.product = ((rev >> 8) & 0x07);
+ ts->input_dev->id.version = ((rev >> 16) & 0xfff);
+ clk_disable(ts->clk);
+
+ __set_bit(EV_KEY, ts->input_dev->evbit);
+ __set_bit(EV_ABS, ts->input_dev->evbit);
+ __set_bit(BTN_TOUCH, ts->input_dev->keybit);
+
+ input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0);
+ input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0);
+ input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0);
+
+ error = input_register_device(ts->input_dev);
+ if (error < 0) {
+ dev_err(dev, "failed input device registration\n");
+ goto error_reg;
+ }
+
+ return 0;
+
+error_reg:
+ input_free_device(ts->input_dev);
+error_input:
+ free_irq(ts->tsc_irq, ts);
+error_irq:
+ clk_put(ts->clk);
+error_clk:
+ iounmap(ts->regs);
+error_map:
+ release_mem_region(ts->res->start, resource_size(ts->res));
+error_res:
+ platform_set_drvdata(pdev, NULL);
+ kfree(ts);
+
+ return error;
+}
+
+static int __devexit tsc_remove(struct platform_device *pdev)
+{
+ struct tsc_data *ts = platform_get_drvdata(pdev);
+
+ input_unregister_device(ts->input_dev);
+ free_irq(ts->tsc_irq, ts);
+ clk_put(ts->clk);
+ iounmap(ts->regs);
+ release_mem_region(ts->res->start, resource_size(ts->res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(ts);
+
+ return 0;
+}
+
+static struct platform_driver tsc_driver = {
+ .probe = tsc_probe,
+ .remove = __devexit_p(tsc_remove),
+ .driver.name = "tnetv107x-ts",
+ .driver.owner = THIS_MODULE,
+};
+
+static int __init tsc_init(void)
+{
+ return platform_driver_register(&tsc_driver);
+}
+
+static void __exit tsc_exit(void)
+{
+ platform_driver_unregister(&tsc_driver);
+}
+
+module_init(tsc_init);
+module_exit(tsc_exit);
+
+MODULE_AUTHOR("Cyril Chemparathy");
+MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
+MODULE_ALIAS("platform: tnetv107x-ts");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
index a644d18c04dc..c8c136cf7bbc 100644
--- a/drivers/input/touchscreen/tps6507x-ts.c
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -335,6 +335,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
dev_err(tsc->dev, "schedule failed");
goto err2;
}
+ platform_set_drvdata(pdev, tps6507x_dev);
return 0;
@@ -358,7 +359,7 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&tsc->work);
destroy_workqueue(tsc->wq);
- input_free_device(input_dev);
+ input_unregister_device(input_dev);
tps6507x_dev->ts = NULL;
kfree(tsc);
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index be23780e8a3e..80467f262331 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -265,7 +265,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tsc2007 *ts;
- struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
+ struct tsc2007_platform_data *pdata = client->dev.platform_data;
struct input_dev *input_dev;
int err;
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 56dc35c94bb1..9ae4c7b16ba7 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -2,6 +2,7 @@
* Wacom W8001 penabled serial touchscreen driver
*
* Copyright (c) 2008 Jaya Kumar
+ * Copyright (c) 2010 Red Hat, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
@@ -30,11 +31,24 @@ MODULE_LICENSE("GPL");
#define W8001_LEAD_BYTE 0x80
#define W8001_TAB_MASK 0x40
#define W8001_TAB_BYTE 0x40
+/* set in first byte of touch data packets */
+#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK)
+#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE)
#define W8001_QUERY_PACKET 0x20
#define W8001_CMD_START '1'
#define W8001_CMD_QUERY '*'
+#define W8001_CMD_TOUCHQUERY '%'
+
+/* length of data packets in bytes, depends on device. */
+#define W8001_PKTLEN_TOUCH93 5
+#define W8001_PKTLEN_TOUCH9A 7
+#define W8001_PKTLEN_TPCPEN 9
+#define W8001_PKTLEN_TPCCTL 11 /* control packet */
+#define W8001_PKTLEN_TOUCH2FG 13
+
+#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */
struct w8001_coord {
u8 rdy;
@@ -48,6 +62,15 @@ struct w8001_coord {
u8 tilt_y;
};
+/* touch query reply packet */
+struct w8001_touch_query {
+ u8 panel_res;
+ u8 capacity_res;
+ u8 sensor_id;
+ u16 x;
+ u16 y;
+};
+
/*
* Per-touchscreen data.
*/
@@ -62,6 +85,9 @@ struct w8001 {
unsigned char response[W8001_MAX_LENGTH];
unsigned char data[W8001_MAX_LENGTH];
char phys[32];
+ int type;
+ unsigned int pktlen;
+ int trkid[2];
};
static void parse_data(u8 *data, struct w8001_coord *coord)
@@ -88,11 +114,98 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
coord->tilt_y = data[8] & 0x7F;
}
+static void parse_touch(struct w8001 *w8001)
+{
+ static int trkid;
+ struct input_dev *dev = w8001->dev;
+ unsigned char *data = w8001->data;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ input_mt_slot(dev, i);
+
+ if (data[0] & (1 << i)) {
+ int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
+ int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
+ /* data[5,6] and [11,12] is finger capacity */
+
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ if (w8001->trkid[i] < 0)
+ w8001->trkid[i] = trkid++ & MAX_TRACKING_ID;
+ } else {
+ w8001->trkid[i] = -1;
+ }
+ input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]);
+ }
+
+ input_sync(dev);
+}
+
+static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
+{
+ memset(query, 0, sizeof(*query));
+
+ query->panel_res = data[1];
+ query->sensor_id = data[2] & 0x7;
+ query->capacity_res = data[7];
+
+ query->x = data[3] << 9;
+ query->x |= data[4] << 2;
+ query->x |= (data[2] >> 5) & 0x3;
+
+ query->y = data[5] << 9;
+ query->y |= data[6] << 2;
+ query->y |= (data[2] >> 3) & 0x3;
+}
+
+static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
+{
+ struct input_dev *dev = w8001->dev;
+
+ /*
+ * We have 1 bit for proximity (rdy) and 3 bits for tip, side,
+ * side2/eraser. If rdy && f2 are set, this can be either pen + side2,
+ * or eraser. assume
+ * - if dev is already in proximity and f2 is toggled → pen + side2
+ * - if dev comes into proximity with f2 set → eraser
+ * If f2 disappears after assuming eraser, fake proximity out for
+ * eraser and in for pen.
+ */
+
+ if (!w8001->type) {
+ w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ } else if (w8001->type == BTN_TOOL_RUBBER) {
+ if (!coord->f2) {
+ input_report_abs(dev, ABS_PRESSURE, 0);
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_key(dev, BTN_STYLUS, 0);
+ input_report_key(dev, BTN_STYLUS2, 0);
+ input_report_key(dev, BTN_TOOL_RUBBER, 0);
+ input_sync(dev);
+ w8001->type = BTN_TOOL_PEN;
+ }
+ } else {
+ input_report_key(dev, BTN_STYLUS2, coord->f2);
+ }
+
+ input_report_abs(dev, ABS_X, coord->x);
+ input_report_abs(dev, ABS_Y, coord->y);
+ input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure);
+ input_report_key(dev, BTN_TOUCH, coord->tsw);
+ input_report_key(dev, BTN_STYLUS, coord->f1);
+ input_report_key(dev, w8001->type, coord->rdy);
+ input_sync(dev);
+
+ if (!coord->rdy)
+ w8001->type = 0;
+}
+
static irqreturn_t w8001_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct w8001 *w8001 = serio_get_drvdata(serio);
- struct input_dev *dev = w8001->dev;
struct w8001_coord coord;
unsigned char tmp;
@@ -105,26 +218,45 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
}
break;
- case 8:
+ case W8001_PKTLEN_TOUCH93 - 1:
+ case W8001_PKTLEN_TOUCH9A - 1:
+ /* ignore one-finger touch packet. */
+ if (w8001->pktlen == w8001->idx)
+ w8001->idx = 0;
+ break;
+
+ /* Pen coordinates packet */
+ case W8001_PKTLEN_TPCPEN - 1:
tmp = w8001->data[0] & W8001_TAB_MASK;
if (unlikely(tmp == W8001_TAB_BYTE))
break;
+ tmp = (w8001->data[0] & W8001_TOUCH_BYTE);
+ if (tmp == W8001_TOUCH_BYTE)
+ break;
+
w8001->idx = 0;
parse_data(w8001->data, &coord);
- input_report_abs(dev, ABS_X, coord.x);
- input_report_abs(dev, ABS_Y, coord.y);
- input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure);
- input_report_key(dev, BTN_TOUCH, coord.tsw);
- input_sync(dev);
+ report_pen_events(w8001, &coord);
break;
- case 10:
+ /* control packet */
+ case W8001_PKTLEN_TPCCTL - 1:
+ tmp = (w8001->data[0] & W8001_TOUCH_MASK);
+ if (tmp == W8001_TOUCH_BYTE)
+ break;
+
w8001->idx = 0;
memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH);
w8001->response_type = W8001_QUERY_PACKET;
complete(&w8001->cmd_done);
break;
+
+ /* 2 finger touch packet */
+ case W8001_PKTLEN_TOUCH2FG - 1:
+ w8001->idx = 0;
+ parse_touch(w8001);
+ break;
}
return IRQ_HANDLED;
@@ -167,6 +299,38 @@ static int w8001_setup(struct w8001 *w8001)
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
+ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
+ if (!error) {
+ struct w8001_touch_query touch;
+
+ parse_touchquery(w8001->response, &touch);
+
+ switch (touch.sensor_id) {
+ case 0:
+ case 2:
+ w8001->pktlen = W8001_PKTLEN_TOUCH93;
+ break;
+ case 1:
+ case 3:
+ case 4:
+ w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+ break;
+ case 5:
+ w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+ input_mt_create_slots(dev, 2);
+ input_set_abs_params(dev, ABS_MT_TRACKING_ID,
+ 0, MAX_TRACKING_ID, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_X,
+ 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y,
+ 0, touch.y, 0, 0);
+ input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
+ 0, 0, 0, 0);
+ break;
+ }
+ }
+
return w8001_command(w8001, W8001_CMD_START, false);
}
@@ -208,6 +372,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
w8001->serio = serio;
w8001->id = serio->id.id;
w8001->dev = input_dev;
+ w8001->trkid[0] = w8001->trkid[1] = -1;
init_completion(&w8001->cmd_done);
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
@@ -221,6 +386,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN);
+ input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER);
+ input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS);
+ input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2);
serio_set_drvdata(serio, w8001);
err = serio_open(serio, drv);
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
index cbfef1ea7e30..6b75c9f660ae 100644
--- a/drivers/input/touchscreen/wm97xx-core.c
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -125,6 +125,8 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
{
int power_adc = 0, auxval;
u16 power = 0;
+ int rc = 0;
+ int timeout = 0;
/* get codec */
mutex_lock(&wm->codec_mutex);
@@ -143,7 +145,9 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
/* Turn polling mode on to read AUX ADC */
wm->pen_probably_down = 1;
- wm->codec->poll_sample(wm, adcsel, &auxval);
+
+ while (rc != RC_VALID && timeout++ < 5)
+ rc = wm->codec->poll_sample(wm, adcsel, &auxval);
if (power_adc)
wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
@@ -152,8 +156,15 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
wm->pen_probably_down = 0;
+ if (timeout >= 5) {
+ dev_err(wm->dev,
+ "timeout reading auxadc %d, disabling digitiser\n",
+ adcsel);
+ wm->codec->dig_enable(wm, false);
+ }
+
mutex_unlock(&wm->codec_mutex);
- return auxval & 0xfff;
+ return (rc == RC_VALID ? auxval & 0xfff : -EBUSY);
}
EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
@@ -684,8 +695,7 @@ static int wm97xx_probe(struct device *dev)
touch_reg_err:
platform_device_put(wm->touch_dev);
touch_err:
- platform_device_unregister(wm->battery_dev);
- wm->battery_dev = NULL;
+ platform_device_del(wm->battery_dev);
batt_reg_err:
platform_device_put(wm->battery_dev);
batt_err:
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
index 947d4afa25ca..30e6195e19d4 100644
--- a/drivers/macintosh/windfarm_pm121.c
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -482,7 +482,7 @@ static s32 pm121_correct(s32 new_setpoint,
new_min += correction->offset;
new_min = (new_min >> 16) + min;
- return max(new_setpoint, max(new_min, 0));
+ return max3(new_setpoint, new_min, 0);
}
static s32 pm121_connect(unsigned int control_id, s32 setpoint)
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 0b61792a2780..2129cdb115dc 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -254,7 +254,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
* Issue the synchronous I/O from a different thread
* to avoid generic_make_request recursion.
*/
- INIT_WORK_ON_STACK(&req.work, do_metadata);
+ INIT_WORK_ONSTACK(&req.work, do_metadata);
queue_work(ps->metadata_wq, &req.work);
flush_workqueue(ps->metadata_wq);
diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c
index 7961d59f5cac..c06b4d50a3dc 100644
--- a/drivers/media/IR/ir-keytable.c
+++ b/drivers/media/IR/ir-keytable.c
@@ -25,14 +25,56 @@
#define IR_KEYPRESS_TIMEOUT 250
/**
+ * ir_create_table() - initializes a scancode table
+ * @rc_tab: the ir_scancode_table to initialize
+ * @name: name to assign to the table
+ * @ir_type: ir type to assign to the new table
+ * @size: initial size of the table
+ * @return: zero on success or a negative error code
+ *
+ * This routine will initialize the ir_scancode_table and will allocate
+ * memory to hold at least the specified number elements.
+ */
+static int ir_create_table(struct ir_scancode_table *rc_tab,
+ const char *name, u64 ir_type, size_t size)
+{
+ rc_tab->name = name;
+ rc_tab->ir_type = ir_type;
+ rc_tab->alloc = roundup_pow_of_two(size * sizeof(struct ir_scancode));
+ rc_tab->size = rc_tab->alloc / sizeof(struct ir_scancode);
+ rc_tab->scan = kmalloc(rc_tab->alloc, GFP_KERNEL);
+ if (!rc_tab->scan)
+ return -ENOMEM;
+
+ IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
+ rc_tab->size, rc_tab->alloc);
+ return 0;
+}
+
+/**
+ * ir_free_table() - frees memory allocated by a scancode table
+ * @rc_tab: the table whose mappings need to be freed
+ *
+ * This routine will free memory alloctaed for key mappings used by given
+ * scancode table.
+ */
+static void ir_free_table(struct ir_scancode_table *rc_tab)
+{
+ rc_tab->size = 0;
+ kfree(rc_tab->scan);
+ rc_tab->scan = NULL;
+}
+
+/**
* ir_resize_table() - resizes a scancode table if necessary
* @rc_tab: the ir_scancode_table to resize
+ * @gfp_flags: gfp flags to use when allocating memory
* @return: zero on success or a negative error code
*
* This routine will shrink the ir_scancode_table if it has lots of
* unused entries and grow it if it is full.
*/
-static int ir_resize_table(struct ir_scancode_table *rc_tab)
+static int ir_resize_table(struct ir_scancode_table *rc_tab, gfp_t gfp_flags)
{
unsigned int oldalloc = rc_tab->alloc;
unsigned int newalloc = oldalloc;
@@ -57,7 +99,7 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab)
if (newalloc == oldalloc)
return 0;
- newscan = kmalloc(newalloc, GFP_ATOMIC);
+ newscan = kmalloc(newalloc, gfp_flags);
if (!newscan) {
IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc);
return -ENOMEM;
@@ -72,26 +114,78 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab)
}
/**
- * ir_do_setkeycode() - internal function to set a keycode in the
- * scancode->keycode table
+ * ir_update_mapping() - set a keycode in the scancode->keycode table
* @dev: the struct input_dev device descriptor
- * @rc_tab: the struct ir_scancode_table to set the keycode in
- * @scancode: the scancode for the ir command
- * @keycode: the keycode for the ir command
- * @resize: whether the keytable may be shrunk
- * @return: -EINVAL if the keycode could not be inserted, otherwise zero.
+ * @rc_tab: scancode table to be adjusted
+ * @index: index of the mapping that needs to be updated
+ * @keycode: the desired keycode
+ * @return: previous keycode assigned to the mapping
+ *
+ * This routine is used to update scancode->keycopde mapping at given
+ * position.
+ */
+static unsigned int ir_update_mapping(struct input_dev *dev,
+ struct ir_scancode_table *rc_tab,
+ unsigned int index,
+ unsigned int new_keycode)
+{
+ int old_keycode = rc_tab->scan[index].keycode;
+ int i;
+
+ /* Did the user wish to remove the mapping? */
+ if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
+ IR_dprintk(1, "#%d: Deleting scan 0x%04x\n",
+ index, rc_tab->scan[index].scancode);
+ rc_tab->len--;
+ memmove(&rc_tab->scan[index], &rc_tab->scan[index+ 1],
+ (rc_tab->len - index) * sizeof(struct ir_scancode));
+ } else {
+ IR_dprintk(1, "#%d: %s scan 0x%04x with key 0x%04x\n",
+ index,
+ old_keycode == KEY_RESERVED ? "New" : "Replacing",
+ rc_tab->scan[index].scancode, new_keycode);
+ rc_tab->scan[index].keycode = new_keycode;
+ __set_bit(new_keycode, dev->keybit);
+ }
+
+ if (old_keycode != KEY_RESERVED) {
+ /* A previous mapping was updated... */
+ __clear_bit(old_keycode, dev->keybit);
+ /* ... but another scancode might use the same keycode */
+ for (i = 0; i < rc_tab->len; i++) {
+ if (rc_tab->scan[i].keycode == old_keycode) {
+ __set_bit(old_keycode, dev->keybit);
+ break;
+ }
+ }
+
+ /* Possibly shrink the keytable, failure is not a problem */
+ ir_resize_table(rc_tab, GFP_ATOMIC);
+ }
+
+ return old_keycode;
+}
+
+/**
+ * ir_locate_scancode() - set a keycode in the scancode->keycode table
+ * @ir_dev: the struct ir_input_dev device descriptor
+ * @rc_tab: scancode table to be searched
+ * @scancode: the desired scancode
+ * @resize: controls whether we allowed to resize the table to
+ * accomodate not yet present scancodes
+ * @return: index of the mapping containing scancode in question
+ * or -1U in case of failure.
*
- * This routine is used internally to manipulate the scancode->keycode table.
- * The caller has to hold @rc_tab->lock.
+ * This routine is used to locate given scancode in ir_scancode_table.
+ * If scancode is not yet present the routine will allocate a new slot
+ * for it.
*/
-static int ir_do_setkeycode(struct input_dev *dev,
- struct ir_scancode_table *rc_tab,
- unsigned scancode, unsigned keycode,
- bool resize)
+static unsigned int ir_establish_scancode(struct ir_input_dev *ir_dev,
+ struct ir_scancode_table *rc_tab,
+ unsigned int scancode,
+ bool resize)
{
unsigned int i;
- int old_keycode = KEY_RESERVED;
- struct ir_input_dev *ir_dev = input_get_drvdata(dev);
/*
* Unfortunately, some hardware-based IR decoders don't provide
@@ -100,65 +194,34 @@ static int ir_do_setkeycode(struct input_dev *dev,
* the provided IR with another one, it is needed to allow loading
* IR tables from other remotes. So,
*/
- if (ir_dev->props && ir_dev->props->scanmask) {
+ if (ir_dev->props && ir_dev->props->scanmask)
scancode &= ir_dev->props->scanmask;
- }
/* First check if we already have a mapping for this ir command */
for (i = 0; i < rc_tab->len; i++) {
+ if (rc_tab->scan[i].scancode == scancode)
+ return i;
+
/* Keytable is sorted from lowest to highest scancode */
- if (rc_tab->scan[i].scancode > scancode)
+ if (rc_tab->scan[i].scancode >= scancode)
break;
- else if (rc_tab->scan[i].scancode < scancode)
- continue;
-
- old_keycode = rc_tab->scan[i].keycode;
- rc_tab->scan[i].keycode = keycode;
-
- /* Did the user wish to remove the mapping? */
- if (keycode == KEY_RESERVED || keycode == KEY_UNKNOWN) {
- IR_dprintk(1, "#%d: Deleting scan 0x%04x\n",
- i, scancode);
- rc_tab->len--;
- memmove(&rc_tab->scan[i], &rc_tab->scan[i + 1],
- (rc_tab->len - i) * sizeof(struct ir_scancode));
- }
-
- /* Possibly shrink the keytable, failure is not a problem */
- ir_resize_table(rc_tab);
- break;
}
- if (old_keycode == KEY_RESERVED && keycode != KEY_RESERVED) {
- /* No previous mapping found, we might need to grow the table */
- if (resize && ir_resize_table(rc_tab))
- return -ENOMEM;
-
- IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n",
- i, scancode, keycode);
+ /* No previous mapping found, we might need to grow the table */
+ if (rc_tab->size == rc_tab->len) {
+ if (!resize || ir_resize_table(rc_tab, GFP_ATOMIC))
+ return -1U;
+ }
- /* i is the proper index to insert our new keycode */
+ /* i is the proper index to insert our new keycode */
+ if (i < rc_tab->len)
memmove(&rc_tab->scan[i + 1], &rc_tab->scan[i],
(rc_tab->len - i) * sizeof(struct ir_scancode));
- rc_tab->scan[i].scancode = scancode;
- rc_tab->scan[i].keycode = keycode;
- rc_tab->len++;
- set_bit(keycode, dev->keybit);
- } else {
- IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n",
- i, scancode, keycode);
- /* A previous mapping was updated... */
- clear_bit(old_keycode, dev->keybit);
- /* ...but another scancode might use the same keycode */
- for (i = 0; i < rc_tab->len; i++) {
- if (rc_tab->scan[i].keycode == old_keycode) {
- set_bit(old_keycode, dev->keybit);
- break;
- }
- }
- }
+ rc_tab->scan[i].scancode = scancode;
+ rc_tab->scan[i].keycode = KEY_RESERVED;
+ rc_tab->len++;
- return 0;
+ return i;
}
/**
@@ -171,17 +234,41 @@ static int ir_do_setkeycode(struct input_dev *dev,
* This routine is used to handle evdev EVIOCSKEY ioctl.
*/
static int ir_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
{
- int rc;
- unsigned long flags;
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
+ unsigned int index;
+ unsigned int scancode;
+ int retval;
+ unsigned long flags;
spin_lock_irqsave(&rc_tab->lock, flags);
- rc = ir_do_setkeycode(dev, rc_tab, scancode, keycode, true);
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+ index = ke->index;
+ if (index >= rc_tab->len) {
+ retval = -EINVAL;
+ goto out;
+ }
+ } else {
+ retval = input_scancode_to_scalar(ke, &scancode);
+ if (retval)
+ goto out;
+
+ index = ir_establish_scancode(ir_dev, rc_tab, scancode, true);
+ if (index >= rc_tab->len) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ }
+
+ *old_keycode = ir_update_mapping(dev, rc_tab, index, ke->keycode);
+
+out:
spin_unlock_irqrestore(&rc_tab->lock, flags);
- return rc;
+ return retval;
}
/**
@@ -189,32 +276,73 @@ static int ir_setkeycode(struct input_dev *dev,
* @dev: the struct input_dev device descriptor
* @to: the struct ir_scancode_table to copy entries to
* @from: the struct ir_scancode_table to copy entries from
- * @return: -EINVAL if all keycodes could not be inserted, otherwise zero.
+ * @return: -ENOMEM if all keycodes could not be inserted, otherwise zero.
*
* This routine is used to handle table initialization.
*/
-static int ir_setkeytable(struct input_dev *dev,
- struct ir_scancode_table *to,
+static int ir_setkeytable(struct ir_input_dev *ir_dev,
const struct ir_scancode_table *from)
{
- struct ir_input_dev *ir_dev = input_get_drvdata(dev);
struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
- unsigned long flags;
- unsigned int i;
- int rc = 0;
+ unsigned int i, index;
+ int rc;
+
+ rc = ir_create_table(&ir_dev->rc_tab,
+ from->name, from->ir_type, from->size);
+ if (rc)
+ return rc;
+
+ IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
+ rc_tab->size, rc_tab->alloc);
- spin_lock_irqsave(&rc_tab->lock, flags);
for (i = 0; i < from->size; i++) {
- rc = ir_do_setkeycode(dev, to, from->scan[i].scancode,
- from->scan[i].keycode, false);
- if (rc)
+ index = ir_establish_scancode(ir_dev, rc_tab,
+ from->scan[i].scancode, false);
+ if (index >= rc_tab->len) {
+ rc = -ENOMEM;
break;
+ }
+
+ ir_update_mapping(ir_dev->input_dev, rc_tab, index,
+ from->scan[i].keycode);
}
- spin_unlock_irqrestore(&rc_tab->lock, flags);
+
+ if (rc)
+ ir_free_table(rc_tab);
+
return rc;
}
/**
+ * ir_lookup_by_scancode() - locate mapping by scancode
+ * @rc_tab: the &struct ir_scancode_table to search
+ * @scancode: scancode to look for in the table
+ * @return: index in the table, -1U if not found
+ *
+ * This routine performs binary search in RC keykeymap table for
+ * given scancode.
+ */
+static unsigned int ir_lookup_by_scancode(const struct ir_scancode_table *rc_tab,
+ unsigned int scancode)
+{
+ unsigned int start = 0;
+ unsigned int end = rc_tab->len - 1;
+ unsigned int mid;
+
+ while (start <= end) {
+ mid = (start + end) / 2;
+ if (rc_tab->scan[mid].scancode < scancode)
+ start = mid + 1;
+ else if (rc_tab->scan[mid].scancode > scancode)
+ end = mid - 1;
+ else
+ return mid;
+ }
+
+ return -1U;
+}
+
+/**
* ir_getkeycode() - get a keycode from the scancode->keycode table
* @dev: the struct input_dev device descriptor
* @scancode: the desired scancode
@@ -224,36 +352,46 @@ static int ir_setkeytable(struct input_dev *dev,
* This routine is used to handle evdev EVIOCGKEY ioctl.
*/
static int ir_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
+ struct input_keymap_entry *ke)
{
- int start, end, mid;
- unsigned long flags;
- int key = KEY_RESERVED;
struct ir_input_dev *ir_dev = input_get_drvdata(dev);
struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
+ struct ir_scancode *entry;
+ unsigned long flags;
+ unsigned int index;
+ unsigned int scancode;
+ int retval;
spin_lock_irqsave(&rc_tab->lock, flags);
- start = 0;
- end = rc_tab->len - 1;
- while (start <= end) {
- mid = (start + end) / 2;
- if (rc_tab->scan[mid].scancode < scancode)
- start = mid + 1;
- else if (rc_tab->scan[mid].scancode > scancode)
- end = mid - 1;
- else {
- key = rc_tab->scan[mid].keycode;
- break;
- }
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+ index = ke->index;
+ } else {
+ retval = input_scancode_to_scalar(ke, &scancode);
+ if (retval)
+ goto out;
+
+ index = ir_lookup_by_scancode(rc_tab, scancode);
+ }
+
+ if (index >= rc_tab->len) {
+ if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
+ IR_dprintk(1, "unknown key for scancode 0x%04x\n",
+ scancode);
+ retval = -EINVAL;
+ goto out;
}
- spin_unlock_irqrestore(&rc_tab->lock, flags);
- if (key == KEY_RESERVED)
- IR_dprintk(1, "unknown key for scancode 0x%04x\n",
- scancode);
+ entry = &rc_tab->scan[index];
- *keycode = key;
- return 0;
+ ke->index = index;
+ ke->keycode = entry->keycode;
+ ke->len = sizeof(entry->scancode);
+ memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode));
+
+out:
+ spin_unlock_irqrestore(&rc_tab->lock, flags);
+ return retval;
}
/**
@@ -268,12 +406,24 @@ static int ir_getkeycode(struct input_dev *dev,
*/
u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode)
{
- int keycode;
+ struct ir_input_dev *ir_dev = input_get_drvdata(dev);
+ struct ir_scancode_table *rc_tab = &ir_dev->rc_tab;
+ unsigned int keycode;
+ unsigned int index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc_tab->lock, flags);
+
+ index = ir_lookup_by_scancode(rc_tab, scancode);
+ keycode = index < rc_tab->len ?
+ rc_tab->scan[index].keycode : KEY_RESERVED;
+
+ spin_unlock_irqrestore(&rc_tab->lock, flags);
- ir_getkeycode(dev, scancode, &keycode);
if (keycode != KEY_RESERVED)
IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n",
dev->name, scancode, keycode);
+
return keycode;
}
EXPORT_SYMBOL_GPL(ir_g_keycode_from_table);
@@ -453,8 +603,8 @@ int __ir_input_register(struct input_dev *input_dev,
goto out_dev;
}
- input_dev->getkeycode = ir_getkeycode;
- input_dev->setkeycode = ir_setkeycode;
+ input_dev->getkeycode_new = ir_getkeycode;
+ input_dev->setkeycode_new = ir_setkeycode;
input_set_drvdata(input_dev, ir_dev);
ir_dev->input_dev = input_dev;
@@ -462,12 +612,6 @@ int __ir_input_register(struct input_dev *input_dev,
spin_lock_init(&ir_dev->keylock);
setup_timer(&ir_dev->timer_keyup, ir_timer_keyup, (unsigned long)ir_dev);
- ir_dev->rc_tab.name = rc_tab->name;
- ir_dev->rc_tab.ir_type = rc_tab->ir_type;
- ir_dev->rc_tab.alloc = roundup_pow_of_two(rc_tab->size *
- sizeof(struct ir_scancode));
- ir_dev->rc_tab.scan = kmalloc(ir_dev->rc_tab.alloc, GFP_KERNEL);
- ir_dev->rc_tab.size = ir_dev->rc_tab.alloc / sizeof(struct ir_scancode);
if (props) {
ir_dev->props = props;
if (props->open)
@@ -476,23 +620,14 @@ int __ir_input_register(struct input_dev *input_dev,
input_dev->close = ir_close;
}
- if (!ir_dev->rc_tab.scan) {
- rc = -ENOMEM;
- goto out_name;
- }
-
- IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
- ir_dev->rc_tab.size, ir_dev->rc_tab.alloc);
-
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
set_bit(EV_MSC, input_dev->evbit);
set_bit(MSC_SCAN, input_dev->mscbit);
- if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
- rc = -ENOMEM;
- goto out_table;
- }
+ rc = ir_setkeytable(ir_dev, rc_tab);
+ if (rc)
+ goto out_name;
rc = ir_register_class(input_dev);
if (rc < 0)
@@ -522,7 +657,7 @@ int __ir_input_register(struct input_dev *input_dev,
out_event:
ir_unregister_class(input_dev);
out_table:
- kfree(ir_dev->rc_tab.scan);
+ ir_free_table(&ir_dev->rc_tab);
out_name:
kfree(ir_dev->driver_name);
out_dev:
@@ -540,7 +675,6 @@ EXPORT_SYMBOL_GPL(__ir_input_register);
void ir_input_unregister(struct input_dev *input_dev)
{
struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
- struct ir_scancode_table *rc_tab;
if (!ir_dev)
return;
@@ -552,10 +686,7 @@ void ir_input_unregister(struct input_dev *input_dev)
if (ir_dev->props->driver_type == RC_DRIVER_IR_RAW)
ir_raw_event_unregister(input_dev);
- rc_tab = &ir_dev->rc_tab;
- rc_tab->size = 0;
- kfree(rc_tab->scan);
- rc_tab->scan = NULL;
+ ir_free_table(&ir_dev->rc_tab);
ir_unregister_class(input_dev);
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index defa786dee34..b8c4b80e4c43 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -338,6 +338,21 @@ static struct resource ab8500_rtc_resources[] = {
},
};
+static struct resource ab8500_poweronkey_db_resources[] = {
+ {
+ .name = "ONKEY_DBF",
+ .start = AB8500_INT_PON_KEY1DB_F,
+ .end = AB8500_INT_PON_KEY1DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ONKEY_DBR",
+ .start = AB8500_INT_PON_KEY1DB_R,
+ .end = AB8500_INT_PON_KEY1DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct mfd_cell ab8500_devs[] = {
{
.name = "ab8500-gpadc",
@@ -354,6 +369,11 @@ static struct mfd_cell ab8500_devs[] = {
{ .name = "ab8500-usb", },
{ .name = "ab8500-pwm", },
{ .name = "ab8500-regulator", },
+ {
+ .name = "ab8500-poweron-key",
+ .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+ .resources = ab8500_poweronkey_db_resources,
+ },
};
int __devinit ab8500_init(struct ab8500 *ab8500)
diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c
index cd164595f08a..49b4d069cbf9 100644
--- a/drivers/mfd/sh_mobile_sdhi.c
+++ b/drivers/mfd/sh_mobile_sdhi.c
@@ -65,7 +65,7 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state)
p->set_pwr(pdev, state);
}
-static int __init sh_mobile_sdhi_probe(struct platform_device *pdev)
+static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
{
struct sh_mobile_sdhi *priv;
struct tmio_mmc_data *mmc_data;
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 720e099e506d..5d0fb60a4c14 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -698,17 +698,17 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl4030_codec",
+ child = add_child(sub_chip_id, "twl4030-audio",
pdata->codec, sizeof(*pdata->codec),
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
- /* Phoenix*/
+ /* Phoenix codec driver is probed directly atm */
if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl6040_codec",
+ child = add_child(sub_chip_id, "twl6040-codec",
pdata->codec, sizeof(*pdata->codec),
false, 0, 0);
if (IS_ERR(child))
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
index add6f67d8032..9a4b196d6deb 100644
--- a/drivers/mfd/twl4030-codec.c
+++ b/drivers/mfd/twl4030-codec.c
@@ -207,14 +207,14 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
if (pdata->audio) {
cell = &codec->cells[childs];
- cell->name = "twl4030_codec_audio";
+ cell->name = "twl4030-codec";
cell->platform_data = pdata->audio;
cell->data_size = sizeof(*pdata->audio);
childs++;
}
if (pdata->vibra) {
cell = &codec->cells[childs];
- cell->name = "twl4030_codec_vibra";
+ cell->name = "twl4030-vibra";
cell->platform_data = pdata->vibra;
cell->data_size = sizeof(*pdata->vibra);
childs++;
@@ -249,14 +249,14 @@ static int __devexit twl4030_codec_remove(struct platform_device *pdev)
return 0;
}
-MODULE_ALIAS("platform:twl4030_codec");
+MODULE_ALIAS("platform:twl4030-audio");
static struct platform_driver twl4030_codec_driver = {
.probe = twl4030_codec_probe,
.remove = __devexit_p(twl4030_codec_remove),
.driver = {
.owner = THIS_MODULE,
- .name = "twl4030_codec",
+ .name = "twl4030-audio",
},
};
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 1f69743b12ec..5a74db75f66f 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -4,7 +4,6 @@
menuconfig MISC_DEVICES
bool "Misc devices"
- default y
---help---
Say Y here to get to see options for device drivers from various
different categories. This option alone does not add any kernel code.
@@ -24,7 +23,8 @@ config AD525X_DPOT
AD5260, AD5262, AD5263, AD5290, AD5291, AD5292, AD5293,
AD7376, AD8400, AD8402, AD8403, ADN2850, AD5241, AD5242,
AD5243, AD5245, AD5246, AD5247, AD5248, AD5280, AD5282,
- ADN2860, AD5273, AD5171, AD5170, AD5172, AD5173
+ ADN2860, AD5273, AD5171, AD5170, AD5172, AD5173, AD5270,
+ AD5271, AD5272, AD5274
digital potentiometer chips.
See Documentation/misc-devices/ad525x_dpot.txt for the
@@ -284,6 +284,16 @@ config SGI_GRU_DEBUG
This option enables addition debugging code for the SGI GRU driver. If
you are unsure, say N.
+config APDS9802ALS
+ tristate "Medfield Avago APDS9802 ALS Sensor module"
+ depends on I2C
+ help
+ If you say yes here you get support for the ALS APDS9802 ambient
+ light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called apds9802als.
+
config ISL29003
tristate "Intersil ISL29003 ambient light sensor"
depends on I2C && SYSFS
@@ -294,6 +304,16 @@ config ISL29003
This driver can also be built as a module. If so, the module
will be called isl29003.
+config ISL29020
+ tristate "Intersil ISL29020 ambient light sensor"
+ depends on I2C
+ help
+ If you say yes here you get support for the Intersil ISL29020
+ ambient light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called isl29020.
+
config SENSORS_TSL2550
tristate "Taos TSL2550 ambient light sensor"
depends on I2C && SYSFS
@@ -314,6 +334,27 @@ config SENSORS_BH1780
This driver can also be built as a module. If so, the module
will be called bh1780gli.
+config SENSORS_BH1770
+ tristate "BH1770GLC / SFH7770 combined ALS - Proximity sensor"
+ depends on I2C
+ ---help---
+ Say Y here if you want to build a driver for BH1770GLC (ROHM) or
+ SFH7770 (Osram) combined ambient light and proximity sensor chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bh1770glc. If unsure, say N here.
+
+config SENSORS_APDS990X
+ tristate "APDS990X combined als and proximity sensors"
+ depends on I2C
+ default n
+ ---help---
+ Say Y here if you want to build a driver for Avago APDS990x
+ combined ambient light and proximity sensor chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apds990x. If unsure, say N here.
+
config HMC6352
tristate "Honeywell HMC6352 compass"
depends on I2C
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 9f2986b4da2f..4be5c6fc5ef4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -16,6 +16,8 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o
+obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
+obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
@@ -23,7 +25,9 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/
obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o
obj-$(CONFIG_HP_ILO) += hpilo.o
+obj-$(CONFIG_APDS9802ALS) += apds9802als.o
obj-$(CONFIG_ISL29003) += isl29003.o
+obj-$(CONFIG_ISL29020) += isl29020.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_DS1682) += ds1682.o
diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c
index 374352af7979..4ff73c215746 100644
--- a/drivers/misc/ad525x_dpot-i2c.c
+++ b/drivers/misc/ad525x_dpot-i2c.c
@@ -102,6 +102,8 @@ static const struct i2c_device_id ad_dpot_id[] = {
{"ad5170", AD5170_ID},
{"ad5172", AD5172_ID},
{"ad5173", AD5173_ID},
+ {"ad5272", AD5272_ID},
+ {"ad5274", AD5274_ID},
{}
};
MODULE_DEVICE_TABLE(i2c, ad_dpot_id);
diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c
index b8c6df9c8437..7f9a55afe05d 100644
--- a/drivers/misc/ad525x_dpot-spi.c
+++ b/drivers/misc/ad525x_dpot-spi.c
@@ -38,6 +38,8 @@ static const struct ad_dpot_id ad_dpot_spi_devlist[] = {
{.name = "ad8402", .devid = AD8402_ID},
{.name = "ad8403", .devid = AD8403_ID},
{.name = "adn2850", .devid = ADN2850_ID},
+ {.name = "ad5270", .devid = AD5270_ID},
+ {.name = "ad5271", .devid = AD5271_ID},
{}
};
@@ -53,13 +55,13 @@ static int write8(void *client, u8 val)
static int write16(void *client, u8 reg, u8 val)
{
u8 data[2] = {reg, val};
- return spi_write(client, data, 1);
+ return spi_write(client, data, 2);
}
static int write24(void *client, u8 reg, u16 val)
{
u8 data[3] = {reg, val >> 8, val};
- return spi_write(client, data, 1);
+ return spi_write(client, data, 3);
}
static int read8(void *client)
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index 5e6fa8449e8b..7cb911028d09 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -29,9 +29,9 @@
* AD5262 2 256 20, 50, 200
* AD5263 4 256 20, 50, 200
* AD5290 1 256 10, 50, 100
- * AD5291 1 256 20
- * AD5292 1 1024 20
- * AD5293 1 1024 20
+ * AD5291 1 256 20, 50, 100 (20-TP)
+ * AD5292 1 1024 20, 50, 100 (20-TP)
+ * AD5293 1 1024 20, 50, 100
* AD7376 1 128 10, 50, 100, 1M
* AD8400 1 256 1, 10, 50, 100
* AD8402 2 256 1, 10, 50, 100
@@ -52,6 +52,10 @@
* AD5170 1 256 2.5, 10, 50, 100 (OTP)
* AD5172 2 256 2.5, 10, 50, 100 (OTP)
* AD5173 2 256 2.5, 10, 50, 100 (OTP)
+ * AD5270 1 1024 20, 50, 100 (50-TP)
+ * AD5271 1 256 20, 50, 100 (50-TP)
+ * AD5272 1 1024 20, 50, 100 (50-TP)
+ * AD5274 1 256 20, 50, 100 (50-TP)
*
* See Documentation/misc-devices/ad525x_dpot.txt for more info.
*
@@ -126,18 +130,38 @@ static inline int dpot_write_r8d16(struct dpot_data *dpot, u8 reg, u16 val)
static s32 dpot_read_spi(struct dpot_data *dpot, u8 reg)
{
unsigned ctrl = 0;
+ int value;
if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) {
if (dpot->feat & F_RDACS_WONLY)
return dpot->rdac_cache[reg & DPOT_RDAC_MASK];
-
if (dpot->uid == DPOT_UID(AD5291_ID) ||
dpot->uid == DPOT_UID(AD5292_ID) ||
- dpot->uid == DPOT_UID(AD5293_ID))
- return dpot_read_r8d8(dpot,
+ dpot->uid == DPOT_UID(AD5293_ID)) {
+
+ value = dpot_read_r8d8(dpot,
DPOT_AD5291_READ_RDAC << 2);
+ if (dpot->uid == DPOT_UID(AD5291_ID))
+ value = value >> 2;
+
+ return value;
+ } else if (dpot->uid == DPOT_UID(AD5270_ID) ||
+ dpot->uid == DPOT_UID(AD5271_ID)) {
+
+ value = dpot_read_r8d8(dpot,
+ DPOT_AD5270_1_2_4_READ_RDAC << 2);
+
+ if (value < 0)
+ return value;
+
+ if (dpot->uid == DPOT_UID(AD5271_ID))
+ value = value >> 2;
+
+ return value;
+ }
+
ctrl = DPOT_SPI_READ_RDAC;
} else if (reg & DPOT_ADDR_EEPROM) {
ctrl = DPOT_SPI_READ_EEPROM;
@@ -153,6 +177,7 @@ static s32 dpot_read_spi(struct dpot_data *dpot, u8 reg)
static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg)
{
+ int value;
unsigned ctrl = 0;
switch (dpot->uid) {
case DPOT_UID(AD5246_ID):
@@ -166,7 +191,7 @@ static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg)
case DPOT_UID(AD5280_ID):
case DPOT_UID(AD5282_ID):
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
- 0 : DPOT_AD5291_RDAC_AB;
+ 0 : DPOT_AD5282_RDAC_AB;
return dpot_read_r8d8(dpot, ctrl);
case DPOT_UID(AD5170_ID):
case DPOT_UID(AD5171_ID):
@@ -175,8 +200,27 @@ static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg)
case DPOT_UID(AD5172_ID):
case DPOT_UID(AD5173_ID):
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
- 0 : DPOT_AD5272_3_A0;
+ 0 : DPOT_AD5172_3_A0;
return dpot_read_r8d8(dpot, ctrl);
+ case DPOT_UID(AD5272_ID):
+ case DPOT_UID(AD5274_ID):
+ dpot_write_r8d8(dpot,
+ (DPOT_AD5270_1_2_4_READ_RDAC << 2), 0);
+
+ value = dpot_read_r8d16(dpot,
+ DPOT_AD5270_1_2_4_RDAC << 2);
+
+ if (value < 0)
+ return value;
+ /*
+ * AD5272/AD5274 returns high byte first, however
+ * underling smbus expects low byte first.
+ */
+ value = swab16(value);
+
+ if (dpot->uid == DPOT_UID(AD5271_ID))
+ value = value >> 2;
+ return value;
default:
if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256))
return dpot_read_r8d16(dpot, (reg & 0xF8) |
@@ -198,7 +242,7 @@ static s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value)
{
unsigned val = 0;
- if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) {
+ if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD | DPOT_ADDR_OTP))) {
if (dpot->feat & F_RDACS_WONLY)
dpot->rdac_cache[reg & DPOT_RDAC_MASK] = value;
@@ -219,11 +263,30 @@ static s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value)
} else {
if (dpot->uid == DPOT_UID(AD5291_ID) ||
dpot->uid == DPOT_UID(AD5292_ID) ||
- dpot->uid == DPOT_UID(AD5293_ID))
+ dpot->uid == DPOT_UID(AD5293_ID)) {
+
+ dpot_write_r8d8(dpot, DPOT_AD5291_CTRLREG << 2,
+ DPOT_AD5291_UNLOCK_CMD);
+
+ if (dpot->uid == DPOT_UID(AD5291_ID))
+ value = value << 2;
+
return dpot_write_r8d8(dpot,
(DPOT_AD5291_RDAC << 2) |
(value >> 8), value & 0xFF);
+ } else if (dpot->uid == DPOT_UID(AD5270_ID) ||
+ dpot->uid == DPOT_UID(AD5271_ID)) {
+ dpot_write_r8d8(dpot,
+ DPOT_AD5270_1_2_4_CTRLREG << 2,
+ DPOT_AD5270_1_2_4_UNLOCK_CMD);
+
+ if (dpot->uid == DPOT_UID(AD5271_ID))
+ value = value << 2;
+ return dpot_write_r8d8(dpot,
+ (DPOT_AD5270_1_2_4_RDAC << 2) |
+ (value >> 8), value & 0xFF);
+ }
val = DPOT_SPI_RDAC | (reg & DPOT_RDAC_MASK);
}
} else if (reg & DPOT_ADDR_EEPROM) {
@@ -243,6 +306,16 @@ static s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value)
val = DPOT_SPI_INC_ALL;
break;
}
+ } else if (reg & DPOT_ADDR_OTP) {
+ if (dpot->uid == DPOT_UID(AD5291_ID) ||
+ dpot->uid == DPOT_UID(AD5292_ID)) {
+ return dpot_write_r8d8(dpot,
+ DPOT_AD5291_STORE_XTPM << 2, 0);
+ } else if (dpot->uid == DPOT_UID(AD5270_ID) ||
+ dpot->uid == DPOT_UID(AD5271_ID)) {
+ return dpot_write_r8d8(dpot,
+ DPOT_AD5270_1_2_4_STORE_XTPM << 2, 0);
+ }
} else
BUG();
@@ -273,7 +346,7 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
case DPOT_UID(AD5280_ID):
case DPOT_UID(AD5282_ID):
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
- 0 : DPOT_AD5291_RDAC_AB;
+ 0 : DPOT_AD5282_RDAC_AB;
return dpot_write_r8d8(dpot, ctrl, value);
break;
case DPOT_UID(AD5171_ID):
@@ -289,12 +362,12 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
case DPOT_UID(AD5172_ID):
case DPOT_UID(AD5173_ID):
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
- 0 : DPOT_AD5272_3_A0;
+ 0 : DPOT_AD5172_3_A0;
if (reg & DPOT_ADDR_OTP) {
tmp = dpot_read_r8d16(dpot, ctrl);
if (tmp >> 14) /* Ready to Program? */
return -EFAULT;
- ctrl |= DPOT_AD5270_2_3_FUSE;
+ ctrl |= DPOT_AD5170_2_3_FUSE;
}
return dpot_write_r8d8(dpot, ctrl, value);
break;
@@ -303,10 +376,25 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
tmp = dpot_read_r8d16(dpot, tmp);
if (tmp >> 14) /* Ready to Program? */
return -EFAULT;
- ctrl = DPOT_AD5270_2_3_FUSE;
+ ctrl = DPOT_AD5170_2_3_FUSE;
}
return dpot_write_r8d8(dpot, ctrl, value);
break;
+ case DPOT_UID(AD5272_ID):
+ case DPOT_UID(AD5274_ID):
+ dpot_write_r8d8(dpot, DPOT_AD5270_1_2_4_CTRLREG << 2,
+ DPOT_AD5270_1_2_4_UNLOCK_CMD);
+
+ if (reg & DPOT_ADDR_OTP)
+ return dpot_write_r8d8(dpot,
+ DPOT_AD5270_1_2_4_STORE_XTPM << 2, 0);
+
+ if (dpot->uid == DPOT_UID(AD5274_ID))
+ value = value << 2;
+
+ return dpot_write_r8d8(dpot, (DPOT_AD5270_1_2_4_RDAC << 2) |
+ (value >> 8), value & 0xFF);
+ break;
default:
if (reg & DPOT_ADDR_CMD)
return dpot_write_d8(dpot, reg);
@@ -320,7 +408,6 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
}
}
-
static s32 dpot_write(struct dpot_data *dpot, u8 reg, u16 value)
{
if (dpot->feat & F_SPI)
diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h
index 78b89fd2e2fd..a662f5987b68 100644
--- a/drivers/misc/ad525x_dpot.h
+++ b/drivers/misc/ad525x_dpot.h
@@ -47,9 +47,9 @@ enum dpot_devid {
AD5258_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 6, 0), /* I2C */
AD5259_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 8, 1),
AD5251_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
- BRDAC0 | BRDAC3, 6, 2),
+ BRDAC1 | BRDAC3, 6, 2),
AD5252_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
- BRDAC0 | BRDAC3, 8, 3),
+ BRDAC1 | BRDAC3, 8, 3),
AD5253_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 4),
AD5254_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
@@ -93,8 +93,10 @@ enum dpot_devid {
BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 23),
AD5290_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
BRDAC0, 8, 24),
- AD5291_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 8, 25),
- AD5292_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 26),
+ AD5291_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT | F_CMD_OTP,
+ BRDAC0, 8, 25),
+ AD5292_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT | F_CMD_OTP,
+ BRDAC0, 10, 26),
AD5293_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 27),
AD7376_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
BRDAC0, 7, 28),
@@ -122,6 +124,12 @@ enum dpot_devid {
AD5170_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 8, 45),
AD5172_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0 | BRDAC1, 8, 46),
AD5173_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0 | BRDAC1, 8, 47),
+ AD5270_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP | F_SPI_16BIT,
+ BRDAC0, 10, 48),
+ AD5271_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP | F_SPI_16BIT,
+ BRDAC0, 8, 49),
+ AD5272_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 10, 50),
+ AD5274_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 8, 51),
};
#define DPOT_RDAC0 0
@@ -165,15 +173,24 @@ enum dpot_devid {
/* AD5291/2/3 use special commands */
#define DPOT_AD5291_RDAC 0x01
#define DPOT_AD5291_READ_RDAC 0x02
+#define DPOT_AD5291_STORE_XTPM 0x03
+#define DPOT_AD5291_CTRLREG 0x06
+#define DPOT_AD5291_UNLOCK_CMD 0x03
-/* AD524x use special commands */
-#define DPOT_AD5291_RDAC_AB 0x80
+/* AD5270/1/2/4 use special commands */
+#define DPOT_AD5270_1_2_4_RDAC 0x01
+#define DPOT_AD5270_1_2_4_READ_RDAC 0x02
+#define DPOT_AD5270_1_2_4_STORE_XTPM 0x03
+#define DPOT_AD5270_1_2_4_CTRLREG 0x07
+#define DPOT_AD5270_1_2_4_UNLOCK_CMD 0x03
+
+#define DPOT_AD5282_RDAC_AB 0x80
#define DPOT_AD5273_FUSE 0x80
-#define DPOT_AD5270_2_3_FUSE 0x20
-#define DPOT_AD5270_2_3_OW 0x08
-#define DPOT_AD5272_3_A0 0x08
-#define DPOT_AD5270_2FUSE 0x80
+#define DPOT_AD5170_2_3_FUSE 0x20
+#define DPOT_AD5170_2_3_OW 0x08
+#define DPOT_AD5172_3_A0 0x08
+#define DPOT_AD5170_2FUSE 0x80
struct dpot_data;
diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c
new file mode 100644
index 000000000000..f9b91ba8900c
--- /dev/null
+++ b/drivers/misc/apds9802als.c
@@ -0,0 +1,347 @@
+/*
+ * apds9802als.c - apds9802 ALS Driver
+ *
+ * Copyright (C) 2009 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/pm_runtime.h>
+
+#define ALS_MIN_RANGE_VAL 1
+#define ALS_MAX_RANGE_VAL 2
+#define POWER_STA_ENABLE 1
+#define POWER_STA_DISABLE 0
+
+#define DRIVER_NAME "apds9802als"
+
+struct als_data {
+ struct mutex mutex;
+};
+
+static ssize_t als_sensing_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, 0x81);
+ if (val < 0)
+ return val;
+ if (val & 1)
+ return sprintf(buf, "4095\n");
+ else
+ return sprintf(buf, "65535\n");
+}
+
+static int als_wait_for_data_ready(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ int retry = 10;
+
+ do {
+ msleep(30);
+ ret = i2c_smbus_read_byte_data(client, 0x86);
+ } while (!(ret & 0x80) && retry--);
+
+ if (!retry) {
+ dev_warn(dev, "timeout waiting for data ready\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static ssize_t als_lux0_input_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct als_data *data = i2c_get_clientdata(client);
+ int ret_val;
+ int temp;
+
+ /* Protect against parallel reads */
+ pm_runtime_get_sync(dev);
+ mutex_lock(&data->mutex);
+
+ /* clear EOC interrupt status */
+ i2c_smbus_write_byte(client, 0x40);
+ /* start measurement */
+ temp = i2c_smbus_read_byte_data(client, 0x81);
+ i2c_smbus_write_byte_data(client, 0x81, temp | 0x08);
+
+ ret_val = als_wait_for_data_ready(dev);
+ if (ret_val < 0)
+ goto failed;
+
+ temp = i2c_smbus_read_byte_data(client, 0x8C); /* LSB data */
+ if (temp < 0) {
+ ret_val = temp;
+ goto failed;
+ }
+ ret_val = i2c_smbus_read_byte_data(client, 0x8D); /* MSB data */
+ if (ret_val < 0)
+ goto failed;
+
+ mutex_unlock(&data->mutex);
+ pm_runtime_put_sync(dev);
+
+ temp = (ret_val << 8) | temp;
+ return sprintf(buf, "%d\n", temp);
+failed:
+ mutex_unlock(&data->mutex);
+ pm_runtime_put_sync(dev);
+ return ret_val;
+}
+
+static ssize_t als_sensing_range_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct als_data *data = i2c_get_clientdata(client);
+ unsigned int ret_val;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ if (val < 4096)
+ val = 1;
+ else if (val < 65536)
+ val = 2;
+ else
+ return -ERANGE;
+
+ pm_runtime_get_sync(dev);
+
+ /* Make sure nobody else reads/modifies/writes 0x81 while we
+ are active */
+ mutex_lock(&data->mutex);
+
+ ret_val = i2c_smbus_read_byte_data(client, 0x81);
+ if (ret_val < 0)
+ goto fail;
+
+ /* Reset the bits before setting them */
+ ret_val = ret_val & 0xFA;
+
+ if (val == 1) /* Setting detection range up to 4k LUX */
+ ret_val = (ret_val | 0x01);
+ else /* Setting detection range up to 64k LUX*/
+ ret_val = (ret_val | 0x00);
+
+ ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val);
+
+ if (ret_val >= 0) {
+ /* All OK */
+ mutex_unlock(&data->mutex);
+ pm_runtime_put_sync(dev);
+ return count;
+ }
+fail:
+ mutex_unlock(&data->mutex);
+ pm_runtime_put_sync(dev);
+ return ret_val;
+}
+
+static int als_set_power_state(struct i2c_client *client, bool on_off)
+{
+ int ret_val;
+ struct als_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->mutex);
+ ret_val = i2c_smbus_read_byte_data(client, 0x80);
+ if (ret_val < 0)
+ goto fail;
+ if (on_off)
+ ret_val = ret_val | 0x01;
+ else
+ ret_val = ret_val & 0xFE;
+ ret_val = i2c_smbus_write_byte_data(client, 0x80, ret_val);
+fail:
+ mutex_unlock(&data->mutex);
+ return ret_val;
+}
+
+static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
+ als_sensing_range_show, als_sensing_range_store);
+static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL);
+
+static struct attribute *mid_att_als[] = {
+ &dev_attr_lux0_sensor_range.attr,
+ &dev_attr_lux0_input.attr,
+ NULL
+};
+
+static struct attribute_group m_als_gr = {
+ .name = "apds9802als",
+ .attrs = mid_att_als
+};
+
+static int als_set_default_config(struct i2c_client *client)
+{
+ int ret_val;
+ /* Write the command and then switch on */
+ ret_val = i2c_smbus_write_byte_data(client, 0x80, 0x01);
+ if (ret_val < 0) {
+ dev_err(&client->dev, "failed default switch on write\n");
+ return ret_val;
+ }
+ /* detection range: 1~64K Lux, maunal measurement */
+ ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x08);
+ if (ret_val < 0)
+ dev_err(&client->dev, "failed default LUX on write\n");
+
+ /* We always get 0 for the 1st measurement after system power on,
+ * so make sure it is finished before user asks for data.
+ */
+ als_wait_for_data_ready(&client->dev);
+
+ return ret_val;
+}
+
+static int apds9802als_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int res;
+ struct als_data *data;
+
+ data = kzalloc(sizeof(struct als_data), GFP_KERNEL);
+ if (data == NULL) {
+ dev_err(&client->dev, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(client, data);
+ res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
+ if (res) {
+ dev_err(&client->dev, "device create file failed\n");
+ goto als_error1;
+ }
+ dev_info(&client->dev, "ALS chip found\n");
+ als_set_default_config(client);
+ mutex_init(&data->mutex);
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_get(&client->dev);
+ pm_runtime_put(&client->dev);
+
+ return res;
+als_error1:
+ i2c_set_clientdata(client, NULL);
+ kfree(data);
+ return res;
+}
+
+static int apds9802als_remove(struct i2c_client *client)
+{
+ struct als_data *data = i2c_get_clientdata(client);
+
+ als_set_power_state(client, false);
+ sysfs_remove_group(&client->dev.kobj, &m_als_gr);
+ kfree(data);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ als_set_power_state(client, false);
+ return 0;
+}
+
+static int apds9802als_resume(struct i2c_client *client)
+{
+ als_set_default_config(client);
+
+ pm_runtime_get(&client->dev);
+ pm_runtime_put(&client->dev);
+ return 0;
+}
+
+static int apds9802als_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ als_set_power_state(client, false);
+ return 0;
+}
+
+static int apds9802als_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ als_set_power_state(client, true);
+ return 0;
+}
+
+static const struct dev_pm_ops apds9802als_pm_ops = {
+ .runtime_suspend = apds9802als_runtime_suspend,
+ .runtime_resume = apds9802als_runtime_resume,
+};
+
+#define APDS9802ALS_PM_OPS (&apds9802als_pm_ops)
+
+#else /* CONFIG_PM */
+#define apds9802als_suspend NULL
+#define apds9802als_resume NULL
+#define APDS9802ALS_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_device_id apds9802als_id[] = {
+ { DRIVER_NAME, 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, apds9802als_id);
+
+static struct i2c_driver apds9802als_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = APDS9802ALS_PM_OPS,
+ },
+ .probe = apds9802als_probe,
+ .remove = apds9802als_remove,
+ .suspend = apds9802als_suspend,
+ .resume = apds9802als_resume,
+ .id_table = apds9802als_id,
+};
+
+static int __init sensor_apds9802als_init(void)
+{
+ return i2c_add_driver(&apds9802als_driver);
+}
+
+static void __exit sensor_apds9802als_exit(void)
+{
+ i2c_del_driver(&apds9802als_driver);
+}
+module_init(sensor_apds9802als_init);
+module_exit(sensor_apds9802als_exit);
+
+MODULE_AUTHOR("Anantha Narayanan <Anantha.Narayanan@intel.com");
+MODULE_DESCRIPTION("Avago apds9802als ALS Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c
new file mode 100644
index 000000000000..200311fea369
--- /dev/null
+++ b/drivers/misc/apds990x.c
@@ -0,0 +1,1295 @@
+/*
+ * This file is part of the APDS990x sensor driver.
+ * Chip is combined proximity and ambient light sensor.
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/i2c/apds990x.h>
+
+/* Register map */
+#define APDS990X_ENABLE 0x00 /* Enable of states and interrupts */
+#define APDS990X_ATIME 0x01 /* ALS ADC time */
+#define APDS990X_PTIME 0x02 /* Proximity ADC time */
+#define APDS990X_WTIME 0x03 /* Wait time */
+#define APDS990X_AILTL 0x04 /* ALS interrupt low threshold low byte */
+#define APDS990X_AILTH 0x05 /* ALS interrupt low threshold hi byte */
+#define APDS990X_AIHTL 0x06 /* ALS interrupt hi threshold low byte */
+#define APDS990X_AIHTH 0x07 /* ALS interrupt hi threshold hi byte */
+#define APDS990X_PILTL 0x08 /* Proximity interrupt low threshold low byte */
+#define APDS990X_PILTH 0x09 /* Proximity interrupt low threshold hi byte */
+#define APDS990X_PIHTL 0x0a /* Proximity interrupt hi threshold low byte */
+#define APDS990X_PIHTH 0x0b /* Proximity interrupt hi threshold hi byte */
+#define APDS990X_PERS 0x0c /* Interrupt persistence filters */
+#define APDS990X_CONFIG 0x0d /* Configuration */
+#define APDS990X_PPCOUNT 0x0e /* Proximity pulse count */
+#define APDS990X_CONTROL 0x0f /* Gain control register */
+#define APDS990X_REV 0x11 /* Revision Number */
+#define APDS990X_ID 0x12 /* Device ID */
+#define APDS990X_STATUS 0x13 /* Device status */
+#define APDS990X_CDATAL 0x14 /* Clear ADC low data register */
+#define APDS990X_CDATAH 0x15 /* Clear ADC high data register */
+#define APDS990X_IRDATAL 0x16 /* IR ADC low data register */
+#define APDS990X_IRDATAH 0x17 /* IR ADC high data register */
+#define APDS990X_PDATAL 0x18 /* Proximity ADC low data register */
+#define APDS990X_PDATAH 0x19 /* Proximity ADC high data register */
+
+/* Control */
+#define APDS990X_MAX_AGAIN 3
+
+/* Enable register */
+#define APDS990X_EN_PIEN (0x1 << 5)
+#define APDS990X_EN_AIEN (0x1 << 4)
+#define APDS990X_EN_WEN (0x1 << 3)
+#define APDS990X_EN_PEN (0x1 << 2)
+#define APDS990X_EN_AEN (0x1 << 1)
+#define APDS990X_EN_PON (0x1 << 0)
+#define APDS990X_EN_DISABLE_ALL 0
+
+/* Status register */
+#define APDS990X_ST_PINT (0x1 << 5)
+#define APDS990X_ST_AINT (0x1 << 4)
+
+/* I2C access types */
+#define APDS990x_CMD_TYPE_MASK (0x03 << 5)
+#define APDS990x_CMD_TYPE_RB (0x00 << 5) /* Repeated byte */
+#define APDS990x_CMD_TYPE_INC (0x01 << 5) /* Auto increment */
+#define APDS990x_CMD_TYPE_SPE (0x03 << 5) /* Special function */
+
+#define APDS990x_ADDR_SHIFT 0
+#define APDS990x_CMD 0x80
+
+/* Interrupt ack commands */
+#define APDS990X_INT_ACK_ALS 0x6
+#define APDS990X_INT_ACK_PS 0x5
+#define APDS990X_INT_ACK_BOTH 0x7
+
+/* ptime */
+#define APDS990X_PTIME_DEFAULT 0xff /* Recommended conversion time 2.7ms*/
+
+/* wtime */
+#define APDS990X_WTIME_DEFAULT 0xee /* ~50ms wait time */
+
+#define APDS990X_TIME_TO_ADC 1024 /* One timetick as ADC count value */
+
+/* Persistence */
+#define APDS990X_APERS_SHIFT 0
+#define APDS990X_PPERS_SHIFT 4
+
+/* Supported ID:s */
+#define APDS990X_ID_0 0x0
+#define APDS990X_ID_4 0x4
+#define APDS990X_ID_29 0x29
+
+/* pgain and pdiode settings */
+#define APDS_PGAIN_1X 0x0
+#define APDS_PDIODE_IR 0x2
+
+#define APDS990X_LUX_OUTPUT_SCALE 10
+
+/* Reverse chip factors for threshold calculation */
+struct reverse_factors {
+ u32 afactor;
+ int cf1;
+ int irf1;
+ int cf2;
+ int irf2;
+};
+
+struct apds990x_chip {
+ struct apds990x_platform_data *pdata;
+ struct i2c_client *client;
+ struct mutex mutex; /* avoid parallel access */
+ struct regulator_bulk_data regs[2];
+ wait_queue_head_t wait;
+
+ int prox_en;
+ bool prox_continuous_mode;
+ bool lux_wait_fresh_res;
+
+ /* Chip parameters */
+ struct apds990x_chip_factors cf;
+ struct reverse_factors rcf;
+ u16 atime; /* als integration time */
+ u16 arate; /* als reporting rate */
+ u16 a_max_result; /* Max possible ADC value with current atime */
+ u8 again_meas; /* Gain used in last measurement */
+ u8 again_next; /* Next calculated gain */
+ u8 pgain;
+ u8 pdiode;
+ u8 pdrive;
+ u8 lux_persistence;
+ u8 prox_persistence;
+
+ u32 lux_raw;
+ u32 lux;
+ u16 lux_clear;
+ u16 lux_ir;
+ u16 lux_calib;
+ u32 lux_thres_hi;
+ u32 lux_thres_lo;
+
+ u32 prox_thres;
+ u16 prox_data;
+ u16 prox_calib;
+
+ char chipname[10];
+ u8 revision;
+};
+
+#define APDS_CALIB_SCALER 8192
+#define APDS_LUX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER)
+#define APDS_PROX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER)
+
+#define APDS_PROX_DEF_THRES 600
+#define APDS_PROX_HYSTERESIS 50
+#define APDS_LUX_DEF_THRES_HI 101
+#define APDS_LUX_DEF_THRES_LO 100
+#define APDS_DEFAULT_PROX_PERS 1
+
+#define APDS_TIMEOUT 2000
+#define APDS_STARTUP_DELAY 25000 /* us */
+#define APDS_RANGE 65535
+#define APDS_PROX_RANGE 1023
+#define APDS_LUX_GAIN_LO_LIMIT 100
+#define APDS_LUX_GAIN_LO_LIMIT_STRICT 25
+
+#define TIMESTEP 87 /* 2.7ms is about 87 / 32 */
+#define TIME_STEP_SCALER 32
+
+#define APDS_LUX_AVERAGING_TIME 50 /* tolerates 50/60Hz ripple */
+#define APDS_LUX_DEFAULT_RATE 200
+
+static const u8 again[] = {1, 8, 16, 120}; /* ALS gain steps */
+static const u8 ir_currents[] = {100, 50, 25, 12}; /* IRled currents in mA */
+
+/* Following two tables must match i.e 10Hz rate means 1 as persistence value */
+static const u16 arates_hz[] = {10, 5, 2, 1};
+static const u8 apersis[] = {1, 2, 4, 5};
+
+/* Regulators */
+static const char reg_vcc[] = "Vdd";
+static const char reg_vled[] = "Vled";
+
+static int apds990x_read_byte(struct apds990x_chip *chip, u8 reg, u8 *data)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+
+ reg &= ~APDS990x_CMD_TYPE_MASK;
+ reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ *data = ret;
+ return (int)ret;
+}
+
+static int apds990x_read_word(struct apds990x_chip *chip, u8 reg, u16 *data)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+
+ reg &= ~APDS990x_CMD_TYPE_MASK;
+ reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC;
+
+ ret = i2c_smbus_read_word_data(client, reg);
+ *data = ret;
+ return (int)ret;
+}
+
+static int apds990x_write_byte(struct apds990x_chip *chip, u8 reg, u8 data)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+
+ reg &= ~APDS990x_CMD_TYPE_MASK;
+ reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB;
+
+ ret = i2c_smbus_write_byte_data(client, reg, data);
+ return (int)ret;
+}
+
+static int apds990x_write_word(struct apds990x_chip *chip, u8 reg, u16 data)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+
+ reg &= ~APDS990x_CMD_TYPE_MASK;
+ reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC;
+
+ ret = i2c_smbus_write_word_data(client, reg, data);
+ return (int)ret;
+}
+
+static int apds990x_mode_on(struct apds990x_chip *chip)
+{
+ /* ALS is mandatory, proximity optional */
+ u8 reg = APDS990X_EN_AIEN | APDS990X_EN_PON | APDS990X_EN_AEN |
+ APDS990X_EN_WEN;
+
+ if (chip->prox_en)
+ reg |= APDS990X_EN_PIEN | APDS990X_EN_PEN;
+
+ return apds990x_write_byte(chip, APDS990X_ENABLE, reg);
+}
+
+static u16 apds990x_lux_to_threshold(struct apds990x_chip *chip, u32 lux)
+{
+ u32 thres;
+ u32 cpl;
+ u32 ir;
+
+ if (lux == 0)
+ return 0;
+ else if (lux == APDS_RANGE)
+ return APDS_RANGE;
+
+ /*
+ * Reported LUX value is a combination of the IR and CLEAR channel
+ * values. However, interrupt threshold is only for clear channel.
+ * This function approximates needed HW threshold value for a given
+ * LUX value in the current lightning type.
+ * IR level compared to visible light varies heavily depending on the
+ * source of the light
+ *
+ * Calculate threshold value for the next measurement period.
+ * Math: threshold = lux * cpl where
+ * cpl = atime * again / (glass_attenuation * device_factor)
+ * (count-per-lux)
+ *
+ * First remove calibration. Division by four is to avoid overflow
+ */
+ lux = lux * (APDS_CALIB_SCALER / 4) / (chip->lux_calib / 4);
+
+ /* Multiplication by 64 is to increase accuracy */
+ cpl = ((u32)chip->atime * (u32)again[chip->again_next] *
+ APDS_PARAM_SCALE * 64) / (chip->cf.ga * chip->cf.df);
+
+ thres = lux * cpl / 64;
+ /*
+ * Convert IR light from the latest result to match with
+ * new gain step. This helps to adapt with the current
+ * source of light.
+ */
+ ir = (u32)chip->lux_ir * (u32)again[chip->again_next] /
+ (u32)again[chip->again_meas];
+
+ /*
+ * Compensate count with IR light impact
+ * IAC1 > IAC2 (see apds990x_get_lux for formulas)
+ */
+ if (chip->lux_clear * APDS_PARAM_SCALE >=
+ chip->rcf.afactor * chip->lux_ir)
+ thres = (chip->rcf.cf1 * thres + chip->rcf.irf1 * ir) /
+ APDS_PARAM_SCALE;
+ else
+ thres = (chip->rcf.cf2 * thres + chip->rcf.irf2 * ir) /
+ APDS_PARAM_SCALE;
+
+ if (thres >= chip->a_max_result)
+ thres = chip->a_max_result - 1;
+ return thres;
+}
+
+static inline int apds990x_set_atime(struct apds990x_chip *chip, u32 time_ms)
+{
+ u8 reg_value;
+
+ chip->atime = time_ms;
+ /* Formula is specified in the data sheet */
+ reg_value = 256 - ((time_ms * TIME_STEP_SCALER) / TIMESTEP);
+ /* Calculate max ADC value for given integration time */
+ chip->a_max_result = (u16)(256 - reg_value) * APDS990X_TIME_TO_ADC;
+ return apds990x_write_byte(chip, APDS990X_ATIME, reg_value);
+}
+
+/* Called always with mutex locked */
+static int apds990x_refresh_pthres(struct apds990x_chip *chip, int data)
+{
+ int ret, lo, hi;
+
+ /* If the chip is not in use, don't try to access it */
+ if (pm_runtime_suspended(&chip->client->dev))
+ return 0;
+
+ if (data < chip->prox_thres) {
+ lo = 0;
+ hi = chip->prox_thres;
+ } else {
+ lo = chip->prox_thres - APDS_PROX_HYSTERESIS;
+ if (chip->prox_continuous_mode)
+ hi = chip->prox_thres;
+ else
+ hi = APDS_RANGE;
+ }
+
+ ret = apds990x_write_word(chip, APDS990X_PILTL, lo);
+ ret |= apds990x_write_word(chip, APDS990X_PIHTL, hi);
+ return ret;
+}
+
+/* Called always with mutex locked */
+static int apds990x_refresh_athres(struct apds990x_chip *chip)
+{
+ int ret;
+ /* If the chip is not in use, don't try to access it */
+ if (pm_runtime_suspended(&chip->client->dev))
+ return 0;
+
+ ret = apds990x_write_word(chip, APDS990X_AILTL,
+ apds990x_lux_to_threshold(chip, chip->lux_thres_lo));
+ ret |= apds990x_write_word(chip, APDS990X_AIHTL,
+ apds990x_lux_to_threshold(chip, chip->lux_thres_hi));
+
+ return ret;
+}
+
+/* Called always with mutex locked */
+static void apds990x_force_a_refresh(struct apds990x_chip *chip)
+{
+ /* This will force ALS interrupt after the next measurement. */
+ apds990x_write_word(chip, APDS990X_AILTL, APDS_LUX_DEF_THRES_LO);
+ apds990x_write_word(chip, APDS990X_AIHTL, APDS_LUX_DEF_THRES_HI);
+}
+
+/* Called always with mutex locked */
+static void apds990x_force_p_refresh(struct apds990x_chip *chip)
+{
+ /* This will force proximity interrupt after the next measurement. */
+ apds990x_write_word(chip, APDS990X_PILTL, APDS_PROX_DEF_THRES - 1);
+ apds990x_write_word(chip, APDS990X_PIHTL, APDS_PROX_DEF_THRES);
+}
+
+/* Called always with mutex locked */
+static int apds990x_calc_again(struct apds990x_chip *chip)
+{
+ int curr_again = chip->again_meas;
+ int next_again = chip->again_meas;
+ int ret = 0;
+
+ /* Calculate suitable als gain */
+ if (chip->lux_clear == chip->a_max_result)
+ next_again -= 2; /* ALS saturated. Decrease gain by 2 steps */
+ else if (chip->lux_clear > chip->a_max_result / 2)
+ next_again--;
+ else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT)
+ next_again += 2; /* Too dark. Increase gain by 2 steps */
+ else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT)
+ next_again++;
+
+ /* Limit gain to available range */
+ if (next_again < 0)
+ next_again = 0;
+ else if (next_again > APDS990X_MAX_AGAIN)
+ next_again = APDS990X_MAX_AGAIN;
+
+ /* Let's check can we trust the measured result */
+ if (chip->lux_clear == chip->a_max_result)
+ /* Result can be totally garbage due to saturation */
+ ret = -ERANGE;
+ else if (next_again != curr_again &&
+ chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT)
+ /*
+ * Gain is changed and measurement result is very small.
+ * Result can be totally garbage due to underflow
+ */
+ ret = -ERANGE;
+
+ chip->again_next = next_again;
+ apds990x_write_byte(chip, APDS990X_CONTROL,
+ (chip->pdrive << 6) |
+ (chip->pdiode << 4) |
+ (chip->pgain << 2) |
+ (chip->again_next << 0));
+
+ /*
+ * Error means bad result -> re-measurement is needed. The forced
+ * refresh uses fastest possible persistence setting to get result
+ * as soon as possible.
+ */
+ if (ret < 0)
+ apds990x_force_a_refresh(chip);
+ else
+ apds990x_refresh_athres(chip);
+
+ return ret;
+}
+
+/* Called always with mutex locked */
+static int apds990x_get_lux(struct apds990x_chip *chip, int clear, int ir)
+{
+ int iac, iac1, iac2; /* IR adjusted counts */
+ u32 lpc; /* Lux per count */
+
+ /* Formulas:
+ * iac1 = CF1 * CLEAR_CH - IRF1 * IR_CH
+ * iac2 = CF2 * CLEAR_CH - IRF2 * IR_CH
+ */
+ iac1 = (chip->cf.cf1 * clear - chip->cf.irf1 * ir) / APDS_PARAM_SCALE;
+ iac2 = (chip->cf.cf2 * clear - chip->cf.irf2 * ir) / APDS_PARAM_SCALE;
+
+ iac = max(iac1, iac2);
+ iac = max(iac, 0);
+
+ lpc = APDS990X_LUX_OUTPUT_SCALE * (chip->cf.df * chip->cf.ga) /
+ (u32)(again[chip->again_meas] * (u32)chip->atime);
+
+ return (iac * lpc) / APDS_PARAM_SCALE;
+}
+
+static int apds990x_ack_int(struct apds990x_chip *chip, u8 mode)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+ u8 reg = APDS990x_CMD | APDS990x_CMD_TYPE_SPE;
+
+ switch (mode & (APDS990X_ST_AINT | APDS990X_ST_PINT)) {
+ case APDS990X_ST_AINT:
+ reg |= APDS990X_INT_ACK_ALS;
+ break;
+ case APDS990X_ST_PINT:
+ reg |= APDS990X_INT_ACK_PS;
+ break;
+ default:
+ reg |= APDS990X_INT_ACK_BOTH;
+ break;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ return (int)ret;
+}
+
+static irqreturn_t apds990x_irq(int irq, void *data)
+{
+ struct apds990x_chip *chip = data;
+ u8 status;
+
+ apds990x_read_byte(chip, APDS990X_STATUS, &status);
+ apds990x_ack_int(chip, status);
+
+ mutex_lock(&chip->mutex);
+ if (!pm_runtime_suspended(&chip->client->dev)) {
+ if (status & APDS990X_ST_AINT) {
+ apds990x_read_word(chip, APDS990X_CDATAL,
+ &chip->lux_clear);
+ apds990x_read_word(chip, APDS990X_IRDATAL,
+ &chip->lux_ir);
+ /* Store used gain for calculations */
+ chip->again_meas = chip->again_next;
+
+ chip->lux_raw = apds990x_get_lux(chip,
+ chip->lux_clear,
+ chip->lux_ir);
+
+ if (apds990x_calc_again(chip) == 0) {
+ /* Result is valid */
+ chip->lux = chip->lux_raw;
+ chip->lux_wait_fresh_res = false;
+ wake_up(&chip->wait);
+ sysfs_notify(&chip->client->dev.kobj,
+ NULL, "lux0_input");
+ }
+ }
+
+ if ((status & APDS990X_ST_PINT) && chip->prox_en) {
+ u16 clr_ch;
+
+ apds990x_read_word(chip, APDS990X_CDATAL, &clr_ch);
+ /*
+ * If ALS channel is saturated at min gain,
+ * proximity gives false posivite values.
+ * Just ignore them.
+ */
+ if (chip->again_meas == 0 &&
+ clr_ch == chip->a_max_result)
+ chip->prox_data = 0;
+ else
+ apds990x_read_word(chip,
+ APDS990X_PDATAL,
+ &chip->prox_data);
+
+ apds990x_refresh_pthres(chip, chip->prox_data);
+ if (chip->prox_data < chip->prox_thres)
+ chip->prox_data = 0;
+ else if (!chip->prox_continuous_mode)
+ chip->prox_data = APDS_PROX_RANGE;
+ sysfs_notify(&chip->client->dev.kobj,
+ NULL, "prox0_raw");
+ }
+ }
+ mutex_unlock(&chip->mutex);
+ return IRQ_HANDLED;
+}
+
+static int apds990x_configure(struct apds990x_chip *chip)
+{
+ /* It is recommended to use disabled mode during these operations */
+ apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL);
+
+ /* conversion and wait times for different state machince states */
+ apds990x_write_byte(chip, APDS990X_PTIME, APDS990X_PTIME_DEFAULT);
+ apds990x_write_byte(chip, APDS990X_WTIME, APDS990X_WTIME_DEFAULT);
+ apds990x_set_atime(chip, APDS_LUX_AVERAGING_TIME);
+
+ apds990x_write_byte(chip, APDS990X_CONFIG, 0);
+
+ /* Persistence levels */
+ apds990x_write_byte(chip, APDS990X_PERS,
+ (chip->lux_persistence << APDS990X_APERS_SHIFT) |
+ (chip->prox_persistence << APDS990X_PPERS_SHIFT));
+
+ apds990x_write_byte(chip, APDS990X_PPCOUNT, chip->pdata->ppcount);
+
+ /* Start with relatively small gain */
+ chip->again_meas = 1;
+ chip->again_next = 1;
+ apds990x_write_byte(chip, APDS990X_CONTROL,
+ (chip->pdrive << 6) |
+ (chip->pdiode << 4) |
+ (chip->pgain << 2) |
+ (chip->again_next << 0));
+ return 0;
+}
+
+static int apds990x_detect(struct apds990x_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+ u8 id;
+
+ ret = apds990x_read_byte(chip, APDS990X_ID, &id);
+ if (ret < 0) {
+ dev_err(&client->dev, "ID read failed\n");
+ return ret;
+ }
+
+ ret = apds990x_read_byte(chip, APDS990X_REV, &chip->revision);
+ if (ret < 0) {
+ dev_err(&client->dev, "REV read failed\n");
+ return ret;
+ }
+
+ switch (id) {
+ case APDS990X_ID_0:
+ case APDS990X_ID_4:
+ case APDS990X_ID_29:
+ snprintf(chip->chipname, sizeof(chip->chipname), "APDS-990x");
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+ return ret;
+}
+
+static int apds990x_chip_on(struct apds990x_chip *chip)
+{
+ int err = regulator_bulk_enable(ARRAY_SIZE(chip->regs),
+ chip->regs);
+ if (err < 0)
+ return err;
+
+ usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY);
+
+ /* Refresh all configs in case of regulators were off */
+ chip->prox_data = 0;
+ apds990x_configure(chip);
+ apds990x_mode_on(chip);
+ return 0;
+}
+
+static int apds990x_chip_off(struct apds990x_chip *chip)
+{
+ apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL);
+ regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
+ return 0;
+}
+
+static ssize_t apds990x_lux_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 result;
+ long timeout;
+
+ if (pm_runtime_suspended(dev))
+ return -EIO;
+
+ timeout = wait_event_interruptible_timeout(chip->wait,
+ !chip->lux_wait_fresh_res,
+ msecs_to_jiffies(APDS_TIMEOUT));
+ if (!timeout)
+ return -EIO;
+
+ mutex_lock(&chip->mutex);
+ result = (chip->lux * chip->lux_calib) / APDS_CALIB_SCALER;
+ if (result > (APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE))
+ result = APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE;
+
+ ret = sprintf(buf, "%d.%d\n",
+ result / APDS990X_LUX_OUTPUT_SCALE,
+ result % APDS990X_LUX_OUTPUT_SCALE);
+ mutex_unlock(&chip->mutex);
+ return ret;
+}
+
+static DEVICE_ATTR(lux0_input, S_IRUGO, apds990x_lux_show, NULL);
+
+static ssize_t apds990x_lux_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", APDS_RANGE);
+}
+
+static DEVICE_ATTR(lux0_sensor_range, S_IRUGO, apds990x_lux_range_show, NULL);
+
+static ssize_t apds990x_lux_calib_format_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", APDS_CALIB_SCALER);
+}
+
+static DEVICE_ATTR(lux0_calibscale_default, S_IRUGO,
+ apds990x_lux_calib_format_show, NULL);
+
+static ssize_t apds990x_lux_calib_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", chip->lux_calib);
+}
+
+static ssize_t apds990x_lux_calib_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (chip->lux_calib > APDS_RANGE)
+ return -EINVAL;
+
+ chip->lux_calib = value;
+
+ return len;
+}
+
+static DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, apds990x_lux_calib_show,
+ apds990x_lux_calib_store);
+
+static ssize_t apds990x_rate_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ int pos = 0;
+ for (i = 0; i < ARRAY_SIZE(arates_hz); i++)
+ pos += sprintf(buf + pos, "%d ", arates_hz[i]);
+ sprintf(buf + pos - 1, "\n");
+ return pos;
+}
+
+static ssize_t apds990x_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->arate);
+}
+
+static int apds990x_set_arate(struct apds990x_chip *chip, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arates_hz); i++)
+ if (rate >= arates_hz[i])
+ break;
+
+ if (i == ARRAY_SIZE(arates_hz))
+ return -EINVAL;
+
+ /* Pick up corresponding persistence value */
+ chip->lux_persistence = apersis[i];
+ chip->arate = arates_hz[i];
+
+ /* If the chip is not in use, don't try to access it */
+ if (pm_runtime_suspended(&chip->client->dev))
+ return 0;
+
+ /* Persistence levels */
+ return apds990x_write_byte(chip, APDS990X_PERS,
+ (chip->lux_persistence << APDS990X_APERS_SHIFT) |
+ (chip->prox_persistence << APDS990X_PPERS_SHIFT));
+}
+
+static ssize_t apds990x_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+ int ret;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ ret = apds990x_set_arate(chip, value);
+ mutex_unlock(&chip->mutex);
+
+ if (ret < 0)
+ return ret;
+ return len;
+}
+
+static DEVICE_ATTR(lux0_rate_avail, S_IRUGO, apds990x_rate_avail, NULL);
+
+static DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, apds990x_rate_show,
+ apds990x_rate_store);
+
+static ssize_t apds990x_prox_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ if (pm_runtime_suspended(dev) || !chip->prox_en)
+ return -EIO;
+
+ mutex_lock(&chip->mutex);
+ ret = sprintf(buf, "%d\n", chip->prox_data);
+ mutex_unlock(&chip->mutex);
+ return ret;
+}
+
+static DEVICE_ATTR(prox0_raw, S_IRUGO, apds990x_prox_show, NULL);
+
+static ssize_t apds990x_prox_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", APDS_PROX_RANGE);
+}
+
+static DEVICE_ATTR(prox0_sensor_range, S_IRUGO, apds990x_prox_range_show, NULL);
+
+static ssize_t apds990x_prox_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->prox_en);
+}
+
+static ssize_t apds990x_prox_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+
+ if (!chip->prox_en)
+ chip->prox_data = 0;
+
+ if (value)
+ chip->prox_en++;
+ else if (chip->prox_en > 0)
+ chip->prox_en--;
+
+ if (!pm_runtime_suspended(dev))
+ apds990x_mode_on(chip);
+ mutex_unlock(&chip->mutex);
+ return len;
+}
+
+static DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, apds990x_prox_enable_show,
+ apds990x_prox_enable_store);
+
+static const char reporting_modes[][9] = {"trigger", "periodic"};
+
+static ssize_t apds990x_prox_reporting_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n",
+ reporting_modes[!!chip->prox_continuous_mode]);
+}
+
+static ssize_t apds990x_prox_reporting_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+
+ if (sysfs_streq(buf, reporting_modes[0]))
+ chip->prox_continuous_mode = 0;
+ else if (sysfs_streq(buf, reporting_modes[1]))
+ chip->prox_continuous_mode = 1;
+ else
+ return -EINVAL;
+ return len;
+}
+
+static DEVICE_ATTR(prox0_reporting_mode, S_IRUGO | S_IWUSR,
+ apds990x_prox_reporting_mode_show,
+ apds990x_prox_reporting_mode_store);
+
+static ssize_t apds990x_prox_reporting_avail_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s %s\n", reporting_modes[0], reporting_modes[1]);
+}
+
+static DEVICE_ATTR(prox0_reporting_mode_avail, S_IRUGO | S_IWUSR,
+ apds990x_prox_reporting_avail_show, NULL);
+
+
+static ssize_t apds990x_lux_thresh_above_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->lux_thres_hi);
+}
+
+static ssize_t apds990x_lux_thresh_below_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->lux_thres_lo);
+}
+
+static ssize_t apds990x_set_lux_thresh(struct apds990x_chip *chip, u32 *target,
+ const char *buf)
+{
+ int ret = 0;
+ unsigned long thresh;
+
+ if (strict_strtoul(buf, 0, &thresh))
+ return -EINVAL;
+
+ if (thresh > APDS_RANGE)
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ *target = thresh;
+ /*
+ * Don't update values in HW if we are still waiting for
+ * first interrupt to come after device handle open call.
+ */
+ if (!chip->lux_wait_fresh_res)
+ apds990x_refresh_athres(chip);
+ mutex_unlock(&chip->mutex);
+ return ret;
+
+}
+
+static ssize_t apds990x_lux_thresh_above_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_hi, buf);
+ if (ret < 0)
+ return ret;
+ return len;
+}
+
+static ssize_t apds990x_lux_thresh_below_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_lo, buf);
+ if (ret < 0)
+ return ret;
+ return len;
+}
+
+static DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR,
+ apds990x_lux_thresh_above_show,
+ apds990x_lux_thresh_above_store);
+
+static DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR,
+ apds990x_lux_thresh_below_show,
+ apds990x_lux_thresh_below_store);
+
+static ssize_t apds990x_prox_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->prox_thres);
+}
+
+static ssize_t apds990x_prox_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if ((value > APDS_RANGE) || (value == 0) ||
+ (value < APDS_PROX_HYSTERESIS))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ chip->prox_thres = value;
+
+ apds990x_force_p_refresh(chip);
+ mutex_unlock(&chip->mutex);
+ return len;
+}
+
+static DEVICE_ATTR(prox0_thresh_above_value, S_IRUGO | S_IWUSR,
+ apds990x_prox_threshold_show,
+ apds990x_prox_threshold_store);
+
+static ssize_t apds990x_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", !pm_runtime_suspended(dev));
+ return 0;
+}
+
+static ssize_t apds990x_power_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+ if (value) {
+ pm_runtime_get_sync(dev);
+ mutex_lock(&chip->mutex);
+ chip->lux_wait_fresh_res = true;
+ apds990x_force_a_refresh(chip);
+ apds990x_force_p_refresh(chip);
+ mutex_unlock(&chip->mutex);
+ } else {
+ if (!pm_runtime_suspended(dev))
+ pm_runtime_put(dev);
+ }
+ return len;
+}
+
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
+ apds990x_power_state_show,
+ apds990x_power_state_store);
+
+static ssize_t apds990x_chip_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct apds990x_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%s %d\n", chip->chipname, chip->revision);
+}
+
+static DEVICE_ATTR(chip_id, S_IRUGO, apds990x_chip_id_show, NULL);
+
+static struct attribute *sysfs_attrs_ctrl[] = {
+ &dev_attr_lux0_calibscale.attr,
+ &dev_attr_lux0_calibscale_default.attr,
+ &dev_attr_lux0_input.attr,
+ &dev_attr_lux0_sensor_range.attr,
+ &dev_attr_lux0_rate.attr,
+ &dev_attr_lux0_rate_avail.attr,
+ &dev_attr_lux0_thresh_above_value.attr,
+ &dev_attr_lux0_thresh_below_value.attr,
+ &dev_attr_prox0_raw_en.attr,
+ &dev_attr_prox0_raw.attr,
+ &dev_attr_prox0_sensor_range.attr,
+ &dev_attr_prox0_thresh_above_value.attr,
+ &dev_attr_prox0_reporting_mode.attr,
+ &dev_attr_prox0_reporting_mode_avail.attr,
+ &dev_attr_chip_id.attr,
+ &dev_attr_power_state.attr,
+ NULL
+};
+
+static struct attribute_group apds990x_attribute_group[] = {
+ {.attrs = sysfs_attrs_ctrl },
+};
+
+static int __devinit apds990x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct apds990x_chip *chip;
+ int err;
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+
+ init_waitqueue_head(&chip->wait);
+ mutex_init(&chip->mutex);
+ chip->pdata = client->dev.platform_data;
+
+ if (chip->pdata == NULL) {
+ dev_err(&client->dev, "platform data is mandatory\n");
+ err = -EINVAL;
+ goto fail1;
+ }
+
+ if (chip->pdata->cf.ga == 0) {
+ /* set uncovered sensor default parameters */
+ chip->cf.ga = 1966; /* 0.48 * APDS_PARAM_SCALE */
+ chip->cf.cf1 = 4096; /* 1.00 * APDS_PARAM_SCALE */
+ chip->cf.irf1 = 9134; /* 2.23 * APDS_PARAM_SCALE */
+ chip->cf.cf2 = 2867; /* 0.70 * APDS_PARAM_SCALE */
+ chip->cf.irf2 = 5816; /* 1.42 * APDS_PARAM_SCALE */
+ chip->cf.df = 52;
+ } else {
+ chip->cf = chip->pdata->cf;
+ }
+
+ /* precalculate inverse chip factors for threshold control */
+ chip->rcf.afactor =
+ (chip->cf.irf1 - chip->cf.irf2) * APDS_PARAM_SCALE /
+ (chip->cf.cf1 - chip->cf.cf2);
+ chip->rcf.cf1 = APDS_PARAM_SCALE * APDS_PARAM_SCALE /
+ chip->cf.cf1;
+ chip->rcf.irf1 = chip->cf.irf1 * APDS_PARAM_SCALE /
+ chip->cf.cf1;
+ chip->rcf.cf2 = APDS_PARAM_SCALE * APDS_PARAM_SCALE /
+ chip->cf.cf2;
+ chip->rcf.irf2 = chip->cf.irf2 * APDS_PARAM_SCALE /
+ chip->cf.cf2;
+
+ /* Set something to start with */
+ chip->lux_thres_hi = APDS_LUX_DEF_THRES_HI;
+ chip->lux_thres_lo = APDS_LUX_DEF_THRES_LO;
+ chip->lux_calib = APDS_LUX_NEUTRAL_CALIB_VALUE;
+
+ chip->prox_thres = APDS_PROX_DEF_THRES;
+ chip->pdrive = chip->pdata->pdrive;
+ chip->pdiode = APDS_PDIODE_IR;
+ chip->pgain = APDS_PGAIN_1X;
+ chip->prox_calib = APDS_PROX_NEUTRAL_CALIB_VALUE;
+ chip->prox_persistence = APDS_DEFAULT_PROX_PERS;
+ chip->prox_continuous_mode = false;
+
+ chip->regs[0].supply = reg_vcc;
+ chip->regs[1].supply = reg_vled;
+
+ err = regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(chip->regs), chip->regs);
+ if (err < 0) {
+ dev_err(&client->dev, "Cannot get regulators\n");
+ goto fail1;
+ }
+
+ err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), chip->regs);
+ if (err < 0) {
+ dev_err(&client->dev, "Cannot enable regulators\n");
+ goto fail2;
+ }
+
+ usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY);
+
+ err = apds990x_detect(chip);
+ if (err < 0) {
+ dev_err(&client->dev, "APDS990X not found\n");
+ goto fail3;
+ }
+
+ pm_runtime_set_active(&client->dev);
+
+ apds990x_configure(chip);
+ apds990x_set_arate(chip, APDS_LUX_DEFAULT_RATE);
+ apds990x_mode_on(chip);
+
+ pm_runtime_enable(&client->dev);
+
+ if (chip->pdata->setup_resources) {
+ err = chip->pdata->setup_resources();
+ if (err) {
+ err = -EINVAL;
+ goto fail3;
+ }
+ }
+
+ err = sysfs_create_group(&chip->client->dev.kobj,
+ apds990x_attribute_group);
+ if (err < 0) {
+ dev_err(&chip->client->dev, "Sysfs registration failed\n");
+ goto fail4;
+ }
+
+ err = request_threaded_irq(client->irq, NULL,
+ apds990x_irq,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT,
+ "apds990x", chip);
+ if (err) {
+ dev_err(&client->dev, "could not get IRQ %d\n",
+ client->irq);
+ goto fail5;
+ }
+ return err;
+fail5:
+ sysfs_remove_group(&chip->client->dev.kobj,
+ &apds990x_attribute_group[0]);
+fail4:
+ if (chip->pdata && chip->pdata->release_resources)
+ chip->pdata->release_resources();
+fail3:
+ regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
+fail2:
+ regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
+fail1:
+ kfree(chip);
+ return err;
+}
+
+static int __devexit apds990x_remove(struct i2c_client *client)
+{
+ struct apds990x_chip *chip = i2c_get_clientdata(client);
+
+ free_irq(client->irq, chip);
+ sysfs_remove_group(&chip->client->dev.kobj,
+ apds990x_attribute_group);
+
+ if (chip->pdata && chip->pdata->release_resources)
+ chip->pdata->release_resources();
+
+ if (!pm_runtime_suspended(&client->dev))
+ apds990x_chip_off(chip);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
+
+ kfree(chip);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int apds990x_suspend(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct apds990x_chip *chip = i2c_get_clientdata(client);
+
+ apds990x_chip_off(chip);
+ return 0;
+}
+
+static int apds990x_resume(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct apds990x_chip *chip = i2c_get_clientdata(client);
+
+ /*
+ * If we were enabled at suspend time, it is expected
+ * everything works nice and smoothly. Chip_on is enough
+ */
+ apds990x_chip_on(chip);
+
+ return 0;
+}
+#else
+#define apds990x_suspend NULL
+#define apds990x_resume NULL
+#define apds990x_shutdown NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int apds990x_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct apds990x_chip *chip = i2c_get_clientdata(client);
+
+ apds990x_chip_off(chip);
+ return 0;
+}
+
+static int apds990x_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct apds990x_chip *chip = i2c_get_clientdata(client);
+
+ apds990x_chip_on(chip);
+ return 0;
+}
+
+#endif
+
+static const struct i2c_device_id apds990x_id[] = {
+ {"apds990x", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, apds990x_id);
+
+static const struct dev_pm_ops apds990x_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(apds990x_suspend, apds990x_resume)
+ SET_RUNTIME_PM_OPS(apds990x_runtime_suspend,
+ apds990x_runtime_resume,
+ NULL)
+};
+
+static struct i2c_driver apds990x_driver = {
+ .driver = {
+ .name = "apds990x",
+ .owner = THIS_MODULE,
+ .pm = &apds990x_pm_ops,
+ },
+ .probe = apds990x_probe,
+ .remove = __devexit_p(apds990x_remove),
+ .id_table = apds990x_id,
+};
+
+static int __init apds990x_init(void)
+{
+ return i2c_add_driver(&apds990x_driver);
+}
+
+static void __exit apds990x_exit(void)
+{
+ i2c_del_driver(&apds990x_driver);
+}
+
+MODULE_DESCRIPTION("APDS990X combined ALS and proximity sensor");
+MODULE_AUTHOR("Samu Onkalo, Nokia Corporation");
+MODULE_LICENSE("GPL v2");
+
+module_init(apds990x_init);
+module_exit(apds990x_exit);
diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c
new file mode 100644
index 000000000000..cee632e645e1
--- /dev/null
+++ b/drivers/misc/bh1770glc.c
@@ -0,0 +1,1413 @@
+/*
+ * This file is part of the ROHM BH1770GLC / OSRAM SFH7770 sensor driver.
+ * Chip is combined proximity and ambient light sensor.
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/i2c/bh1770glc.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#define BH1770_ALS_CONTROL 0x80 /* ALS operation mode control */
+#define BH1770_PS_CONTROL 0x81 /* PS operation mode control */
+#define BH1770_I_LED 0x82 /* active LED and LED1, LED2 current */
+#define BH1770_I_LED3 0x83 /* LED3 current setting */
+#define BH1770_ALS_PS_MEAS 0x84 /* Forced mode trigger */
+#define BH1770_PS_MEAS_RATE 0x85 /* PS meas. rate at stand alone mode */
+#define BH1770_ALS_MEAS_RATE 0x86 /* ALS meas. rate at stand alone mode */
+#define BH1770_PART_ID 0x8a /* Part number and revision ID */
+#define BH1770_MANUFACT_ID 0x8b /* Manufacturerer ID */
+#define BH1770_ALS_DATA_0 0x8c /* ALS DATA low byte */
+#define BH1770_ALS_DATA_1 0x8d /* ALS DATA high byte */
+#define BH1770_ALS_PS_STATUS 0x8e /* Measurement data and int status */
+#define BH1770_PS_DATA_LED1 0x8f /* PS data from LED1 */
+#define BH1770_PS_DATA_LED2 0x90 /* PS data from LED2 */
+#define BH1770_PS_DATA_LED3 0x91 /* PS data from LED3 */
+#define BH1770_INTERRUPT 0x92 /* Interrupt setting */
+#define BH1770_PS_TH_LED1 0x93 /* PS interrupt threshold for LED1 */
+#define BH1770_PS_TH_LED2 0x94 /* PS interrupt threshold for LED2 */
+#define BH1770_PS_TH_LED3 0x95 /* PS interrupt threshold for LED3 */
+#define BH1770_ALS_TH_UP_0 0x96 /* ALS upper threshold low byte */
+#define BH1770_ALS_TH_UP_1 0x97 /* ALS upper threshold high byte */
+#define BH1770_ALS_TH_LOW_0 0x98 /* ALS lower threshold low byte */
+#define BH1770_ALS_TH_LOW_1 0x99 /* ALS lower threshold high byte */
+
+/* MANUFACT_ID */
+#define BH1770_MANUFACT_ROHM 0x01
+#define BH1770_MANUFACT_OSRAM 0x03
+
+/* PART_ID */
+#define BH1770_PART 0x90
+#define BH1770_PART_MASK 0xf0
+#define BH1770_REV_MASK 0x0f
+#define BH1770_REV_SHIFT 0
+#define BH1770_REV_0 0x00
+#define BH1770_REV_1 0x01
+
+/* Operating modes for both */
+#define BH1770_STANDBY 0x00
+#define BH1770_FORCED 0x02
+#define BH1770_STANDALONE 0x03
+#define BH1770_SWRESET (0x01 << 2)
+
+#define BH1770_PS_TRIG_MEAS (1 << 0)
+#define BH1770_ALS_TRIG_MEAS (1 << 1)
+
+/* Interrupt control */
+#define BH1770_INT_OUTPUT_MODE (1 << 3) /* 0 = latched */
+#define BH1770_INT_POLARITY (1 << 2) /* 1 = active high */
+#define BH1770_INT_ALS_ENA (1 << 1)
+#define BH1770_INT_PS_ENA (1 << 0)
+
+/* Interrupt status */
+#define BH1770_INT_LED1_DATA (1 << 0)
+#define BH1770_INT_LED1_INT (1 << 1)
+#define BH1770_INT_LED2_DATA (1 << 2)
+#define BH1770_INT_LED2_INT (1 << 3)
+#define BH1770_INT_LED3_DATA (1 << 4)
+#define BH1770_INT_LED3_INT (1 << 5)
+#define BH1770_INT_LEDS_INT ((1 << 1) | (1 << 3) | (1 << 5))
+#define BH1770_INT_ALS_DATA (1 << 6)
+#define BH1770_INT_ALS_INT (1 << 7)
+
+/* Led channels */
+#define BH1770_LED1 0x00
+
+#define BH1770_DISABLE 0
+#define BH1770_ENABLE 1
+#define BH1770_PROX_CHANNELS 1
+
+#define BH1770_LUX_DEFAULT_RATE 1 /* Index to lux rate table */
+#define BH1770_PROX_DEFAULT_RATE 1 /* Direct HW value =~ 50Hz */
+#define BH1770_PROX_DEF_RATE_THRESH 6 /* Direct HW value =~ 5 Hz */
+#define BH1770_STARTUP_DELAY 50
+#define BH1770_RESET_TIME 10
+#define BH1770_TIMEOUT 2100 /* Timeout in 2.1 seconds */
+
+#define BH1770_LUX_RANGE 65535
+#define BH1770_PROX_RANGE 255
+#define BH1770_COEF_SCALER 1024
+#define BH1770_CALIB_SCALER 8192
+#define BH1770_LUX_NEUTRAL_CALIB_VALUE (1 * BH1770_CALIB_SCALER)
+#define BH1770_LUX_DEF_THRES 1000
+#define BH1770_PROX_DEF_THRES 70
+#define BH1770_PROX_DEF_ABS_THRES 100
+#define BH1770_DEFAULT_PERSISTENCE 10
+#define BH1770_PROX_MAX_PERSISTENCE 50
+#define BH1770_LUX_GA_SCALE 16384
+#define BH1770_LUX_CF_SCALE 2048 /* CF ChipFactor */
+#define BH1770_NEUTRAL_CF BH1770_LUX_CF_SCALE
+#define BH1770_LUX_CORR_SCALE 4096
+
+#define PROX_ABOVE_THRESHOLD 1
+#define PROX_BELOW_THRESHOLD 0
+
+#define PROX_IGNORE_LUX_LIMIT 500
+
+struct bh1770_chip {
+ struct bh1770_platform_data *pdata;
+ char chipname[10];
+ u8 revision;
+ struct i2c_client *client;
+ struct regulator_bulk_data regs[2];
+ struct mutex mutex; /* avoid parallel access */
+ wait_queue_head_t wait;
+
+ bool int_mode_prox;
+ bool int_mode_lux;
+ struct delayed_work prox_work;
+ u32 lux_cf; /* Chip specific factor */
+ u32 lux_ga;
+ u32 lux_calib;
+ int lux_rate_index;
+ u32 lux_corr;
+ u16 lux_data_raw;
+ u16 lux_threshold_hi;
+ u16 lux_threshold_lo;
+ u16 lux_thres_hi_onchip;
+ u16 lux_thres_lo_onchip;
+ bool lux_wait_result;
+
+ int prox_enable_count;
+ u16 prox_coef;
+ u16 prox_const;
+ int prox_rate;
+ int prox_rate_threshold;
+ u8 prox_persistence;
+ u8 prox_persistence_counter;
+ u8 prox_data;
+ u8 prox_threshold;
+ u8 prox_threshold_hw;
+ bool prox_force_update;
+ u8 prox_abs_thres;
+ u8 prox_led;
+};
+
+static const char reg_vcc[] = "Vcc";
+static const char reg_vleds[] = "Vleds";
+
+/*
+ * Supported stand alone rates in ms from chip data sheet
+ * {10, 20, 30, 40, 70, 100, 200, 500, 1000, 2000};
+ */
+static const s16 prox_rates_hz[] = {100, 50, 33, 25, 14, 10, 5, 2};
+static const s16 prox_rates_ms[] = {10, 20, 30, 40, 70, 100, 200, 500};
+
+/* Supported IR-led currents in mA */
+static const u8 prox_curr_ma[] = {5, 10, 20, 50, 100, 150, 200};
+
+/*
+ * Supported stand alone rates in ms from chip data sheet
+ * {100, 200, 500, 1000, 2000};
+ */
+static const s16 lux_rates_hz[] = {10, 5, 2, 1, 0};
+
+/*
+ * interrupt control functions are called while keeping chip->mutex
+ * excluding module probe / remove
+ */
+static inline int bh1770_lux_interrupt_control(struct bh1770_chip *chip,
+ int lux)
+{
+ chip->int_mode_lux = lux;
+ /* Set interrupt modes, interrupt active low, latched */
+ return i2c_smbus_write_byte_data(chip->client,
+ BH1770_INTERRUPT,
+ (lux << 1) | chip->int_mode_prox);
+}
+
+static inline int bh1770_prox_interrupt_control(struct bh1770_chip *chip,
+ int ps)
+{
+ chip->int_mode_prox = ps;
+ return i2c_smbus_write_byte_data(chip->client,
+ BH1770_INTERRUPT,
+ (chip->int_mode_lux << 1) | (ps << 0));
+}
+
+/* chip->mutex is always kept here */
+static int bh1770_lux_rate(struct bh1770_chip *chip, int rate_index)
+{
+ /* sysfs may call this when the chip is powered off */
+ if (pm_runtime_suspended(&chip->client->dev))
+ return 0;
+
+ /* Proper proximity response needs fastest lux rate (100ms) */
+ if (chip->prox_enable_count)
+ rate_index = 0;
+
+ return i2c_smbus_write_byte_data(chip->client,
+ BH1770_ALS_MEAS_RATE,
+ rate_index);
+}
+
+static int bh1770_prox_rate(struct bh1770_chip *chip, int mode)
+{
+ int rate;
+
+ rate = (mode == PROX_ABOVE_THRESHOLD) ?
+ chip->prox_rate_threshold : chip->prox_rate;
+
+ return i2c_smbus_write_byte_data(chip->client,
+ BH1770_PS_MEAS_RATE,
+ rate);
+}
+
+/* InfraredLED is controlled by the chip during proximity scanning */
+static inline int bh1770_led_cfg(struct bh1770_chip *chip)
+{
+ /* LED cfg, current for leds 1 and 2 */
+ return i2c_smbus_write_byte_data(chip->client,
+ BH1770_I_LED,
+ (BH1770_LED1 << 6) |
+ (BH1770_LED_5mA << 3) |
+ chip->prox_led);
+}
+
+/*
+ * Following two functions converts raw ps values from HW to normalized
+ * values. Purpose is to compensate differences between different sensor
+ * versions and variants so that result means about the same between
+ * versions.
+ */
+static inline u8 bh1770_psraw_to_adjusted(struct bh1770_chip *chip, u8 psraw)
+{
+ u16 adjusted;
+ adjusted = (u16)(((u32)(psraw + chip->prox_const) * chip->prox_coef) /
+ BH1770_COEF_SCALER);
+ if (adjusted > BH1770_PROX_RANGE)
+ adjusted = BH1770_PROX_RANGE;
+ return adjusted;
+}
+
+static inline u8 bh1770_psadjusted_to_raw(struct bh1770_chip *chip, u8 ps)
+{
+ u16 raw;
+
+ raw = (((u32)ps * BH1770_COEF_SCALER) / chip->prox_coef);
+ if (raw > chip->prox_const)
+ raw = raw - chip->prox_const;
+ else
+ raw = 0;
+ return raw;
+}
+
+/*
+ * Following two functions converts raw lux values from HW to normalized
+ * values. Purpose is to compensate differences between different sensor
+ * versions and variants so that result means about the same between
+ * versions. Chip->mutex is kept when this is called.
+ */
+static int bh1770_prox_set_threshold(struct bh1770_chip *chip)
+{
+ u8 tmp = 0;
+
+ /* sysfs may call this when the chip is powered off */
+ if (pm_runtime_suspended(&chip->client->dev))
+ return 0;
+
+ tmp = bh1770_psadjusted_to_raw(chip, chip->prox_threshold);
+ chip->prox_threshold_hw = tmp;
+
+ return i2c_smbus_write_byte_data(chip->client, BH1770_PS_TH_LED1,
+ tmp);
+}
+
+static inline u16 bh1770_lux_raw_to_adjusted(struct bh1770_chip *chip, u16 raw)
+{
+ u32 lux;
+ lux = ((u32)raw * chip->lux_corr) / BH1770_LUX_CORR_SCALE;
+ return min(lux, (u32)BH1770_LUX_RANGE);
+}
+
+static inline u16 bh1770_lux_adjusted_to_raw(struct bh1770_chip *chip,
+ u16 adjusted)
+{
+ return (u32)adjusted * BH1770_LUX_CORR_SCALE / chip->lux_corr;
+}
+
+/* chip->mutex is kept when this is called */
+static int bh1770_lux_update_thresholds(struct bh1770_chip *chip,
+ u16 threshold_hi, u16 threshold_lo)
+{
+ u8 data[4];
+ int ret;
+
+ /* sysfs may call this when the chip is powered off */
+ if (pm_runtime_suspended(&chip->client->dev))
+ return 0;
+
+ /*
+ * Compensate threshold values with the correction factors if not
+ * set to minimum or maximum.
+ * Min & max values disables interrupts.
+ */
+ if (threshold_hi != BH1770_LUX_RANGE && threshold_hi != 0)
+ threshold_hi = bh1770_lux_adjusted_to_raw(chip, threshold_hi);
+
+ if (threshold_lo != BH1770_LUX_RANGE && threshold_lo != 0)
+ threshold_lo = bh1770_lux_adjusted_to_raw(chip, threshold_lo);
+
+ if (chip->lux_thres_hi_onchip == threshold_hi &&
+ chip->lux_thres_lo_onchip == threshold_lo)
+ return 0;
+
+ chip->lux_thres_hi_onchip = threshold_hi;
+ chip->lux_thres_lo_onchip = threshold_lo;
+
+ data[0] = threshold_hi;
+ data[1] = threshold_hi >> 8;
+ data[2] = threshold_lo;
+ data[3] = threshold_lo >> 8;
+
+ ret = i2c_smbus_write_i2c_block_data(chip->client,
+ BH1770_ALS_TH_UP_0,
+ ARRAY_SIZE(data),
+ data);
+ return ret;
+}
+
+static int bh1770_lux_get_result(struct bh1770_chip *chip)
+{
+ u16 data;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_DATA_0);
+ if (ret < 0)
+ return ret;
+
+ data = ret & 0xff;
+ ret = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_DATA_1);
+ if (ret < 0)
+ return ret;
+
+ chip->lux_data_raw = data | ((ret & 0xff) << 8);
+
+ return 0;
+}
+
+/* Calculate correction value which contains chip and device specific parts */
+static u32 bh1770_get_corr_value(struct bh1770_chip *chip)
+{
+ u32 tmp;
+ /* Impact of glass attenuation correction */
+ tmp = (BH1770_LUX_CORR_SCALE * chip->lux_ga) / BH1770_LUX_GA_SCALE;
+ /* Impact of chip factor correction */
+ tmp = (tmp * chip->lux_cf) / BH1770_LUX_CF_SCALE;
+ /* Impact of Device specific calibration correction */
+ tmp = (tmp * chip->lux_calib) / BH1770_CALIB_SCALER;
+ return tmp;
+}
+
+static int bh1770_lux_read_result(struct bh1770_chip *chip)
+{
+ bh1770_lux_get_result(chip);
+ return bh1770_lux_raw_to_adjusted(chip, chip->lux_data_raw);
+}
+
+/*
+ * Chip on / off functions are called while keeping mutex except probe
+ * or remove phase
+ */
+static int bh1770_chip_on(struct bh1770_chip *chip)
+{
+ int ret = regulator_bulk_enable(ARRAY_SIZE(chip->regs),
+ chip->regs);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2);
+
+ /* Reset the chip */
+ i2c_smbus_write_byte_data(chip->client, BH1770_ALS_CONTROL,
+ BH1770_SWRESET);
+ usleep_range(BH1770_RESET_TIME, BH1770_RESET_TIME * 2);
+
+ /*
+ * ALS is started always since proximity needs als results
+ * for realibility estimation.
+ * Let's assume dark until the first ALS measurement is ready.
+ */
+ chip->lux_data_raw = 0;
+ chip->prox_data = 0;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ BH1770_ALS_CONTROL, BH1770_STANDALONE);
+
+ /* Assume reset defaults */
+ chip->lux_thres_hi_onchip = BH1770_LUX_RANGE;
+ chip->lux_thres_lo_onchip = 0;
+
+ return ret;
+}
+
+static void bh1770_chip_off(struct bh1770_chip *chip)
+{
+ i2c_smbus_write_byte_data(chip->client,
+ BH1770_INTERRUPT, BH1770_DISABLE);
+ i2c_smbus_write_byte_data(chip->client,
+ BH1770_ALS_CONTROL, BH1770_STANDBY);
+ i2c_smbus_write_byte_data(chip->client,
+ BH1770_PS_CONTROL, BH1770_STANDBY);
+ regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
+}
+
+/* chip->mutex is kept when this is called */
+static int bh1770_prox_mode_control(struct bh1770_chip *chip)
+{
+ if (chip->prox_enable_count) {
+ chip->prox_force_update = true; /* Force immediate update */
+
+ bh1770_lux_rate(chip, chip->lux_rate_index);
+ bh1770_prox_set_threshold(chip);
+ bh1770_led_cfg(chip);
+ bh1770_prox_rate(chip, PROX_BELOW_THRESHOLD);
+ bh1770_prox_interrupt_control(chip, BH1770_ENABLE);
+ i2c_smbus_write_byte_data(chip->client,
+ BH1770_PS_CONTROL, BH1770_STANDALONE);
+ } else {
+ chip->prox_data = 0;
+ bh1770_lux_rate(chip, chip->lux_rate_index);
+ bh1770_prox_interrupt_control(chip, BH1770_DISABLE);
+ i2c_smbus_write_byte_data(chip->client,
+ BH1770_PS_CONTROL, BH1770_STANDBY);
+ }
+ return 0;
+}
+
+/* chip->mutex is kept when this is called */
+static int bh1770_prox_read_result(struct bh1770_chip *chip)
+{
+ int ret;
+ bool above;
+ u8 mode;
+
+ ret = i2c_smbus_read_byte_data(chip->client, BH1770_PS_DATA_LED1);
+ if (ret < 0)
+ goto out;
+
+ if (ret > chip->prox_threshold_hw)
+ above = true;
+ else
+ above = false;
+
+ /*
+ * when ALS levels goes above limit, proximity result may be
+ * false proximity. Thus ignore the result. With real proximity
+ * there is a shadow causing low als levels.
+ */
+ if (chip->lux_data_raw > PROX_IGNORE_LUX_LIMIT)
+ ret = 0;
+
+ chip->prox_data = bh1770_psraw_to_adjusted(chip, ret);
+
+ /* Strong proximity level or force mode requires immediate response */
+ if (chip->prox_data >= chip->prox_abs_thres ||
+ chip->prox_force_update)
+ chip->prox_persistence_counter = chip->prox_persistence;
+
+ chip->prox_force_update = false;
+
+ /* Persistence filttering to reduce false proximity events */
+ if (likely(above)) {
+ if (chip->prox_persistence_counter < chip->prox_persistence) {
+ chip->prox_persistence_counter++;
+ ret = -ENODATA;
+ } else {
+ mode = PROX_ABOVE_THRESHOLD;
+ ret = 0;
+ }
+ } else {
+ chip->prox_persistence_counter = 0;
+ mode = PROX_BELOW_THRESHOLD;
+ chip->prox_data = 0;
+ ret = 0;
+ }
+
+ /* Set proximity detection rate based on above or below value */
+ if (ret == 0) {
+ bh1770_prox_rate(chip, mode);
+ sysfs_notify(&chip->client->dev.kobj, NULL, "prox0_raw");
+ }
+out:
+ return ret;
+}
+
+static int bh1770_detect(struct bh1770_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+ u8 manu, part;
+
+ ret = i2c_smbus_read_byte_data(client, BH1770_MANUFACT_ID);
+ if (ret < 0)
+ goto error;
+ manu = (u8)ret;
+
+ ret = i2c_smbus_read_byte_data(client, BH1770_PART_ID);
+ if (ret < 0)
+ goto error;
+ part = (u8)ret;
+
+ chip->revision = (part & BH1770_REV_MASK) >> BH1770_REV_SHIFT;
+ chip->prox_coef = BH1770_COEF_SCALER;
+ chip->prox_const = 0;
+ chip->lux_cf = BH1770_NEUTRAL_CF;
+
+ if ((manu == BH1770_MANUFACT_ROHM) &&
+ ((part & BH1770_PART_MASK) == BH1770_PART)) {
+ snprintf(chip->chipname, sizeof(chip->chipname), "BH1770GLC");
+ return 0;
+ }
+
+ if ((manu == BH1770_MANUFACT_OSRAM) &&
+ ((part & BH1770_PART_MASK) == BH1770_PART)) {
+ snprintf(chip->chipname, sizeof(chip->chipname), "SFH7770");
+ /* Values selected by comparing different versions */
+ chip->prox_coef = 819; /* 0.8 * BH1770_COEF_SCALER */
+ chip->prox_const = 40;
+ return 0;
+ }
+
+ ret = -ENODEV;
+error:
+ dev_dbg(&client->dev, "BH1770 or SFH7770 not found\n");
+
+ return ret;
+}
+
+/*
+ * This work is re-scheduled at every proximity interrupt.
+ * If this work is running, it means that there hasn't been any
+ * proximity interrupt in time. Situation is handled as no-proximity.
+ * It would be nice to have low-threshold interrupt or interrupt
+ * when measurement and hi-threshold are both 0. But neither of those exists.
+ * This is a workaroud for missing HW feature.
+ */
+
+static void bh1770_prox_work(struct work_struct *work)
+{
+ struct bh1770_chip *chip =
+ container_of(work, struct bh1770_chip, prox_work.work);
+
+ mutex_lock(&chip->mutex);
+ bh1770_prox_read_result(chip);
+ mutex_unlock(&chip->mutex);
+}
+
+/* This is threaded irq handler */
+static irqreturn_t bh1770_irq(int irq, void *data)
+{
+ struct bh1770_chip *chip = data;
+ int status;
+ int rate = 0;
+
+ mutex_lock(&chip->mutex);
+ status = i2c_smbus_read_byte_data(chip->client, BH1770_ALS_PS_STATUS);
+
+ /* Acknowledge interrupt by reading this register */
+ i2c_smbus_read_byte_data(chip->client, BH1770_INTERRUPT);
+
+ /*
+ * Check if there is fresh data available for als.
+ * If this is the very first data, update thresholds after that.
+ */
+ if (status & BH1770_INT_ALS_DATA) {
+ bh1770_lux_get_result(chip);
+ if (unlikely(chip->lux_wait_result)) {
+ chip->lux_wait_result = false;
+ wake_up(&chip->wait);
+ bh1770_lux_update_thresholds(chip,
+ chip->lux_threshold_hi,
+ chip->lux_threshold_lo);
+ }
+ }
+
+ /* Disable interrupt logic to guarantee acknowledgement */
+ i2c_smbus_write_byte_data(chip->client, BH1770_INTERRUPT,
+ (0 << 1) | (0 << 0));
+
+ if ((status & BH1770_INT_ALS_INT))
+ sysfs_notify(&chip->client->dev.kobj, NULL, "lux0_input");
+
+ if (chip->int_mode_prox && (status & BH1770_INT_LEDS_INT)) {
+ rate = prox_rates_ms[chip->prox_rate_threshold];
+ bh1770_prox_read_result(chip);
+ }
+
+ /* Re-enable interrupt logic */
+ i2c_smbus_write_byte_data(chip->client, BH1770_INTERRUPT,
+ (chip->int_mode_lux << 1) |
+ (chip->int_mode_prox << 0));
+ mutex_unlock(&chip->mutex);
+
+ /*
+ * Can't cancel work while keeping mutex since the work uses the
+ * same mutex.
+ */
+ if (rate) {
+ /*
+ * Simulate missing no-proximity interrupt 50ms after the
+ * next expected interrupt time.
+ */
+ cancel_delayed_work_sync(&chip->prox_work);
+ schedule_delayed_work(&chip->prox_work,
+ msecs_to_jiffies(rate + 50));
+ }
+ return IRQ_HANDLED;
+}
+
+static ssize_t bh1770_power_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+ size_t ret;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ if (value) {
+ pm_runtime_get_sync(dev);
+
+ ret = bh1770_lux_rate(chip, chip->lux_rate_index);
+ ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE);
+
+ if (ret < 0) {
+ pm_runtime_put(dev);
+ goto leave;
+ }
+
+ /* This causes interrupt after the next measurement cycle */
+ bh1770_lux_update_thresholds(chip, BH1770_LUX_DEF_THRES,
+ BH1770_LUX_DEF_THRES);
+ /* Inform that we are waiting for a result from ALS */
+ chip->lux_wait_result = true;
+ bh1770_prox_mode_control(chip);
+ } else if (!pm_runtime_suspended(dev)) {
+ pm_runtime_put(dev);
+ }
+ ret = count;
+leave:
+ mutex_unlock(&chip->mutex);
+ return ret;
+}
+
+static ssize_t bh1770_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", !pm_runtime_suspended(dev));
+}
+
+static ssize_t bh1770_lux_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ ssize_t ret;
+ long timeout;
+
+ if (pm_runtime_suspended(dev))
+ return -EIO; /* Chip is not enabled at all */
+
+ timeout = wait_event_interruptible_timeout(chip->wait,
+ !chip->lux_wait_result,
+ msecs_to_jiffies(BH1770_TIMEOUT));
+ if (!timeout)
+ return -EIO;
+
+ mutex_lock(&chip->mutex);
+ ret = sprintf(buf, "%d\n", bh1770_lux_read_result(chip));
+ mutex_unlock(&chip->mutex);
+
+ return ret;
+}
+
+static ssize_t bh1770_lux_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", BH1770_LUX_RANGE);
+}
+
+static ssize_t bh1770_prox_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ /* Assume no proximity. Sensor will tell real state soon */
+ if (!chip->prox_enable_count)
+ chip->prox_data = 0;
+
+ if (value)
+ chip->prox_enable_count++;
+ else if (chip->prox_enable_count > 0)
+ chip->prox_enable_count--;
+ else
+ goto leave;
+
+ /* Run control only when chip is powered on */
+ if (!pm_runtime_suspended(dev))
+ bh1770_prox_mode_control(chip);
+leave:
+ mutex_unlock(&chip->mutex);
+ return count;
+}
+
+static ssize_t bh1770_prox_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ ssize_t len;
+
+ mutex_lock(&chip->mutex);
+ len = sprintf(buf, "%d\n", chip->prox_enable_count);
+ mutex_unlock(&chip->mutex);
+ return len;
+}
+
+static ssize_t bh1770_prox_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ mutex_lock(&chip->mutex);
+ if (chip->prox_enable_count && !pm_runtime_suspended(dev))
+ ret = sprintf(buf, "%d\n", chip->prox_data);
+ else
+ ret = -EIO;
+ mutex_unlock(&chip->mutex);
+ return ret;
+}
+
+static ssize_t bh1770_prox_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", BH1770_PROX_RANGE);
+}
+
+static ssize_t bh1770_get_prox_rate_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ int pos = 0;
+ for (i = 0; i < ARRAY_SIZE(prox_rates_hz); i++)
+ pos += sprintf(buf + pos, "%d ", prox_rates_hz[i]);
+ sprintf(buf + pos - 1, "\n");
+ return pos;
+}
+
+static ssize_t bh1770_get_prox_rate_above(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", prox_rates_hz[chip->prox_rate_threshold]);
+}
+
+static ssize_t bh1770_get_prox_rate_below(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", prox_rates_hz[chip->prox_rate]);
+}
+
+static int bh1770_prox_rate_validate(int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(prox_rates_hz) - 1; i++)
+ if (rate >= prox_rates_hz[i])
+ break;
+ return i;
+}
+
+static ssize_t bh1770_set_prox_rate_above(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ chip->prox_rate_threshold = bh1770_prox_rate_validate(value);
+ mutex_unlock(&chip->mutex);
+ return count;
+}
+
+static ssize_t bh1770_set_prox_rate_below(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ chip->prox_rate = bh1770_prox_rate_validate(value);
+ mutex_unlock(&chip->mutex);
+ return count;
+}
+
+static ssize_t bh1770_get_prox_thres(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->prox_threshold);
+}
+
+static ssize_t bh1770_set_prox_thres(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+ int ret;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+ if (value > BH1770_PROX_RANGE)
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ chip->prox_threshold = value;
+ ret = bh1770_prox_set_threshold(chip);
+ mutex_unlock(&chip->mutex);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t bh1770_prox_persistence_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", chip->prox_persistence);
+}
+
+static ssize_t bh1770_prox_persistence_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value > BH1770_PROX_MAX_PERSISTENCE)
+ return -EINVAL;
+
+ chip->prox_persistence = value;
+
+ return len;
+}
+
+static ssize_t bh1770_prox_abs_thres_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", chip->prox_abs_thres);
+}
+
+static ssize_t bh1770_prox_abs_thres_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value > BH1770_PROX_RANGE)
+ return -EINVAL;
+
+ chip->prox_abs_thres = value;
+
+ return len;
+}
+
+static ssize_t bh1770_chip_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%s rev %d\n", chip->chipname, chip->revision);
+}
+
+static ssize_t bh1770_lux_calib_default_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", BH1770_CALIB_SCALER);
+}
+
+static ssize_t bh1770_lux_calib_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ ssize_t len;
+
+ mutex_lock(&chip->mutex);
+ len = sprintf(buf, "%u\n", chip->lux_calib);
+ mutex_unlock(&chip->mutex);
+ return len;
+}
+
+static ssize_t bh1770_lux_calib_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long value;
+ u32 old_calib;
+ u32 new_corr;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ old_calib = chip->lux_calib;
+ chip->lux_calib = value;
+ new_corr = bh1770_get_corr_value(chip);
+ if (new_corr == 0) {
+ chip->lux_calib = old_calib;
+ mutex_unlock(&chip->mutex);
+ return -EINVAL;
+ }
+ chip->lux_corr = new_corr;
+ /* Refresh thresholds on HW after changing correction value */
+ bh1770_lux_update_thresholds(chip, chip->lux_threshold_hi,
+ chip->lux_threshold_lo);
+
+ mutex_unlock(&chip->mutex);
+
+ return len;
+}
+
+static ssize_t bh1770_get_lux_rate_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ int pos = 0;
+ for (i = 0; i < ARRAY_SIZE(lux_rates_hz); i++)
+ pos += sprintf(buf + pos, "%d ", lux_rates_hz[i]);
+ sprintf(buf + pos - 1, "\n");
+ return pos;
+}
+
+static ssize_t bh1770_get_lux_rate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", lux_rates_hz[chip->lux_rate_index]);
+}
+
+static ssize_t bh1770_set_lux_rate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ unsigned long rate_hz;
+ int ret, i;
+
+ if (strict_strtoul(buf, 0, &rate_hz))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(lux_rates_hz) - 1; i++)
+ if (rate_hz >= lux_rates_hz[i])
+ break;
+
+ mutex_lock(&chip->mutex);
+ chip->lux_rate_index = i;
+ ret = bh1770_lux_rate(chip, i);
+ mutex_unlock(&chip->mutex);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t bh1770_get_lux_thresh_above(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->lux_threshold_hi);
+}
+
+static ssize_t bh1770_get_lux_thresh_below(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", chip->lux_threshold_lo);
+}
+
+static ssize_t bh1770_set_lux_thresh(struct bh1770_chip *chip, u16 *target,
+ const char *buf)
+{
+ int ret = 0;
+ unsigned long thresh;
+
+ if (strict_strtoul(buf, 0, &thresh))
+ return -EINVAL;
+
+ if (thresh > BH1770_LUX_RANGE)
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ *target = thresh;
+ /*
+ * Don't update values in HW if we are still waiting for
+ * first interrupt to come after device handle open call.
+ */
+ if (!chip->lux_wait_result)
+ ret = bh1770_lux_update_thresholds(chip,
+ chip->lux_threshold_hi,
+ chip->lux_threshold_lo);
+ mutex_unlock(&chip->mutex);
+ return ret;
+
+}
+
+static ssize_t bh1770_set_lux_thresh_above(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ int ret = bh1770_set_lux_thresh(chip, &chip->lux_threshold_hi, buf);
+ if (ret < 0)
+ return ret;
+ return len;
+}
+
+static ssize_t bh1770_set_lux_thresh_below(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct bh1770_chip *chip = dev_get_drvdata(dev);
+ int ret = bh1770_set_lux_thresh(chip, &chip->lux_threshold_lo, buf);
+ if (ret < 0)
+ return ret;
+ return len;
+}
+
+static DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, bh1770_prox_enable_show,
+ bh1770_prox_enable_store);
+static DEVICE_ATTR(prox0_thresh_above1_value, S_IRUGO | S_IWUSR,
+ bh1770_prox_abs_thres_show,
+ bh1770_prox_abs_thres_store);
+static DEVICE_ATTR(prox0_thresh_above0_value, S_IRUGO | S_IWUSR,
+ bh1770_get_prox_thres,
+ bh1770_set_prox_thres);
+static DEVICE_ATTR(prox0_raw, S_IRUGO, bh1770_prox_result_show, NULL);
+static DEVICE_ATTR(prox0_sensor_range, S_IRUGO, bh1770_prox_range_show, NULL);
+static DEVICE_ATTR(prox0_thresh_above_count, S_IRUGO | S_IWUSR,
+ bh1770_prox_persistence_show,
+ bh1770_prox_persistence_store);
+static DEVICE_ATTR(prox0_rate_above, S_IRUGO | S_IWUSR,
+ bh1770_get_prox_rate_above,
+ bh1770_set_prox_rate_above);
+static DEVICE_ATTR(prox0_rate_below, S_IRUGO | S_IWUSR,
+ bh1770_get_prox_rate_below,
+ bh1770_set_prox_rate_below);
+static DEVICE_ATTR(prox0_rate_avail, S_IRUGO, bh1770_get_prox_rate_avail, NULL);
+
+static DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, bh1770_lux_calib_show,
+ bh1770_lux_calib_store);
+static DEVICE_ATTR(lux0_calibscale_default, S_IRUGO,
+ bh1770_lux_calib_default_show,
+ NULL);
+static DEVICE_ATTR(lux0_input, S_IRUGO, bh1770_lux_result_show, NULL);
+static DEVICE_ATTR(lux0_sensor_range, S_IRUGO, bh1770_lux_range_show, NULL);
+static DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, bh1770_get_lux_rate,
+ bh1770_set_lux_rate);
+static DEVICE_ATTR(lux0_rate_avail, S_IRUGO, bh1770_get_lux_rate_avail, NULL);
+static DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR,
+ bh1770_get_lux_thresh_above,
+ bh1770_set_lux_thresh_above);
+static DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR,
+ bh1770_get_lux_thresh_below,
+ bh1770_set_lux_thresh_below);
+static DEVICE_ATTR(chip_id, S_IRUGO, bh1770_chip_id_show, NULL);
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, bh1770_power_state_show,
+ bh1770_power_state_store);
+
+
+static struct attribute *sysfs_attrs[] = {
+ &dev_attr_lux0_calibscale.attr,
+ &dev_attr_lux0_calibscale_default.attr,
+ &dev_attr_lux0_input.attr,
+ &dev_attr_lux0_sensor_range.attr,
+ &dev_attr_lux0_rate.attr,
+ &dev_attr_lux0_rate_avail.attr,
+ &dev_attr_lux0_thresh_above_value.attr,
+ &dev_attr_lux0_thresh_below_value.attr,
+ &dev_attr_prox0_raw.attr,
+ &dev_attr_prox0_sensor_range.attr,
+ &dev_attr_prox0_raw_en.attr,
+ &dev_attr_prox0_thresh_above_count.attr,
+ &dev_attr_prox0_rate_above.attr,
+ &dev_attr_prox0_rate_below.attr,
+ &dev_attr_prox0_rate_avail.attr,
+ &dev_attr_prox0_thresh_above0_value.attr,
+ &dev_attr_prox0_thresh_above1_value.attr,
+ &dev_attr_chip_id.attr,
+ &dev_attr_power_state.attr,
+ NULL
+};
+
+static struct attribute_group bh1770_attribute_group = {
+ .attrs = sysfs_attrs
+};
+
+static int __devinit bh1770_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bh1770_chip *chip;
+ int err;
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+
+ mutex_init(&chip->mutex);
+ init_waitqueue_head(&chip->wait);
+ INIT_DELAYED_WORK(&chip->prox_work, bh1770_prox_work);
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "platform data is mandatory\n");
+ err = -EINVAL;
+ goto fail1;
+ }
+
+ chip->pdata = client->dev.platform_data;
+ chip->lux_calib = BH1770_LUX_NEUTRAL_CALIB_VALUE;
+ chip->lux_rate_index = BH1770_LUX_DEFAULT_RATE;
+ chip->lux_threshold_lo = BH1770_LUX_DEF_THRES;
+ chip->lux_threshold_hi = BH1770_LUX_DEF_THRES;
+
+ if (chip->pdata->glass_attenuation == 0)
+ chip->lux_ga = BH1770_NEUTRAL_GA;
+ else
+ chip->lux_ga = chip->pdata->glass_attenuation;
+
+ chip->prox_threshold = BH1770_PROX_DEF_THRES;
+ chip->prox_led = chip->pdata->led_def_curr;
+ chip->prox_abs_thres = BH1770_PROX_DEF_ABS_THRES;
+ chip->prox_persistence = BH1770_DEFAULT_PERSISTENCE;
+ chip->prox_rate_threshold = BH1770_PROX_DEF_RATE_THRESH;
+ chip->prox_rate = BH1770_PROX_DEFAULT_RATE;
+ chip->prox_data = 0;
+
+ chip->regs[0].supply = reg_vcc;
+ chip->regs[1].supply = reg_vleds;
+
+ err = regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(chip->regs), chip->regs);
+ if (err < 0) {
+ dev_err(&client->dev, "Cannot get regulators\n");
+ goto fail1;
+ }
+
+ err = regulator_bulk_enable(ARRAY_SIZE(chip->regs),
+ chip->regs);
+ if (err < 0) {
+ dev_err(&client->dev, "Cannot enable regulators\n");
+ goto fail2;
+ }
+
+ usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2);
+ err = bh1770_detect(chip);
+ if (err < 0)
+ goto fail3;
+
+ /* Start chip */
+ bh1770_chip_on(chip);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ chip->lux_corr = bh1770_get_corr_value(chip);
+ if (chip->lux_corr == 0) {
+ dev_err(&client->dev, "Improper correction values\n");
+ err = -EINVAL;
+ goto fail3;
+ }
+
+ if (chip->pdata->setup_resources) {
+ err = chip->pdata->setup_resources();
+ if (err) {
+ err = -EINVAL;
+ goto fail3;
+ }
+ }
+
+ err = sysfs_create_group(&chip->client->dev.kobj,
+ &bh1770_attribute_group);
+ if (err < 0) {
+ dev_err(&chip->client->dev, "Sysfs registration failed\n");
+ goto fail4;
+ }
+
+ /*
+ * Chip needs level triggered interrupt to work. However,
+ * level triggering doesn't work always correctly with power
+ * management. Select both
+ */
+ err = request_threaded_irq(client->irq, NULL,
+ bh1770_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
+ IRQF_TRIGGER_LOW,
+ "bh1770", chip);
+ if (err) {
+ dev_err(&client->dev, "could not get IRQ %d\n",
+ client->irq);
+ goto fail5;
+ }
+ regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
+ return err;
+fail5:
+ sysfs_remove_group(&chip->client->dev.kobj,
+ &bh1770_attribute_group);
+fail4:
+ if (chip->pdata->release_resources)
+ chip->pdata->release_resources();
+fail3:
+ regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
+fail2:
+ regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
+fail1:
+ kfree(chip);
+ return err;
+}
+
+static int __devexit bh1770_remove(struct i2c_client *client)
+{
+ struct bh1770_chip *chip = i2c_get_clientdata(client);
+
+ free_irq(client->irq, chip);
+
+ sysfs_remove_group(&chip->client->dev.kobj,
+ &bh1770_attribute_group);
+
+ if (chip->pdata->release_resources)
+ chip->pdata->release_resources();
+
+ cancel_delayed_work_sync(&chip->prox_work);
+
+ if (!pm_runtime_suspended(&client->dev))
+ bh1770_chip_off(chip);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
+ kfree(chip);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bh1770_suspend(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct bh1770_chip *chip = i2c_get_clientdata(client);
+
+ bh1770_chip_off(chip);
+
+ return 0;
+}
+
+static int bh1770_resume(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct bh1770_chip *chip = i2c_get_clientdata(client);
+ int ret = 0;
+
+ bh1770_chip_on(chip);
+
+ if (!pm_runtime_suspended(dev)) {
+ /*
+ * If we were enabled at suspend time, it is expected
+ * everything works nice and smoothly
+ */
+ ret = bh1770_lux_rate(chip, chip->lux_rate_index);
+ ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE);
+
+ /* This causes interrupt after the next measurement cycle */
+ bh1770_lux_update_thresholds(chip, BH1770_LUX_DEF_THRES,
+ BH1770_LUX_DEF_THRES);
+ /* Inform that we are waiting for a result from ALS */
+ chip->lux_wait_result = true;
+ bh1770_prox_mode_control(chip);
+ }
+ return ret;
+}
+
+#else
+#define bh1770_suspend NULL
+#define bh1770_shutdown NULL
+#define bh1770_resume NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int bh1770_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct bh1770_chip *chip = i2c_get_clientdata(client);
+
+ bh1770_chip_off(chip);
+
+ return 0;
+}
+
+static int bh1770_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct bh1770_chip *chip = i2c_get_clientdata(client);
+
+ bh1770_chip_on(chip);
+
+ return 0;
+}
+#endif
+
+static const struct i2c_device_id bh1770_id[] = {
+ {"bh1770glc", 0 },
+ {"sfh7770", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bh1770_id);
+
+static const struct dev_pm_ops bh1770_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(bh1770_suspend, bh1770_resume)
+ SET_RUNTIME_PM_OPS(bh1770_runtime_suspend, bh1770_runtime_resume, NULL)
+};
+
+static struct i2c_driver bh1770_driver = {
+ .driver = {
+ .name = "bh1770glc",
+ .owner = THIS_MODULE,
+ .pm = &bh1770_pm_ops,
+ },
+ .probe = bh1770_probe,
+ .remove = __devexit_p(bh1770_remove),
+ .id_table = bh1770_id,
+};
+
+static int __init bh1770_init(void)
+{
+ return i2c_add_driver(&bh1770_driver);
+}
+
+static void __exit bh1770_exit(void)
+{
+ i2c_del_driver(&bh1770_driver);
+}
+
+MODULE_DESCRIPTION("BH1770GLC / SFH7770 combined ALS and proximity sensor");
+MODULE_AUTHOR("Samu Onkalo, Nokia Corporation");
+MODULE_LICENSE("GPL v2");
+
+module_init(bh1770_init);
+module_exit(bh1770_exit);
diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c
index af2497ae5fe3..0a53500636c9 100644
--- a/drivers/misc/ibmasm/ibmasmfs.c
+++ b/drivers/misc/ibmasm/ibmasmfs.c
@@ -146,6 +146,7 @@ static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
struct inode *ret = new_inode(sb);
if (ret) {
+ ret->i_ino = get_next_ino();
ret->i_mode = mode;
ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
}
diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c
new file mode 100644
index 000000000000..34fe835921c4
--- /dev/null
+++ b/drivers/misc/isl29020.c
@@ -0,0 +1,248 @@
+/*
+ * isl29020.c - Intersil ALS Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/pm_runtime.h>
+
+static DEFINE_MUTEX(mutex);
+
+static ssize_t als_sensing_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, 0x00);
+
+ if (val < 0)
+ return val;
+ return sprintf(buf, "%d000\n", 1 << (2 * (val & 3)));
+
+}
+
+static ssize_t als_lux_input_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret_val, val;
+ unsigned long int lux;
+ int temp;
+
+ pm_runtime_get_sync(dev);
+ msleep(100);
+
+ mutex_lock(&mutex);
+ temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */
+ if (temp < 0) {
+ pm_runtime_put_sync(dev);
+ mutex_unlock(&mutex);
+ return temp;
+ }
+
+ ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */
+ mutex_unlock(&mutex);
+
+ if (ret_val < 0) {
+ pm_runtime_put_sync(dev);
+ return ret_val;
+ }
+
+ ret_val |= temp << 8;
+ val = i2c_smbus_read_byte_data(client, 0x00);
+ pm_runtime_put_sync(dev);
+ if (val < 0)
+ return val;
+ lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536;
+ return sprintf(buf, "%ld\n", lux);
+}
+
+static ssize_t als_sensing_range_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned int ret_val;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+ if (val < 1 || val > 64000)
+ return -EINVAL;
+
+ /* Pick the smallest sensor range that will meet our requirements */
+ if (val <= 1000)
+ val = 1;
+ else if (val <= 4000)
+ val = 2;
+ else if (val <= 16000)
+ val = 3;
+ else
+ val = 4;
+
+ ret_val = i2c_smbus_read_byte_data(client, 0x00);
+
+ ret_val &= 0xFC; /*reset the bit before setting them */
+ ret_val |= val - 1;
+ ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val);
+
+ if (ret_val < 0)
+ return ret_val;
+ return count;
+}
+
+static void als_set_power_state(struct i2c_client *client, int enable)
+{
+ int ret_val;
+
+ ret_val = i2c_smbus_read_byte_data(client, 0x00);
+ if (ret_val < 0)
+ return;
+
+ if (enable)
+ ret_val |= 0x80;
+ else
+ ret_val &= 0x7F;
+
+ i2c_smbus_write_byte_data(client, 0x00, ret_val);
+}
+
+static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
+ als_sensing_range_show, als_sensing_range_store);
+static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL);
+
+static struct attribute *mid_att_als[] = {
+ &dev_attr_lux0_sensor_range.attr,
+ &dev_attr_lux0_input.attr,
+ NULL
+};
+
+static struct attribute_group m_als_gr = {
+ .name = "isl29020",
+ .attrs = mid_att_als
+};
+
+static int als_set_default_config(struct i2c_client *client)
+{
+ int retval;
+
+ retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0);
+ if (retval < 0) {
+ dev_err(&client->dev, "default write failed.");
+ return retval;
+ }
+ return 0;;
+}
+
+static int isl29020_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int res;
+
+ res = als_set_default_config(client);
+ if (res < 0)
+ return res;
+
+ res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
+ if (res) {
+ dev_err(&client->dev, "isl29020: device create file failed\n");
+ return res;
+ }
+ dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name);
+ als_set_power_state(client, 0);
+ pm_runtime_enable(&client->dev);
+ return res;
+}
+
+static int isl29020_remove(struct i2c_client *client)
+{
+ struct als_data *data = i2c_get_clientdata(client);
+ sysfs_remove_group(&client->dev.kobj, &m_als_gr);
+ kfree(data);
+ return 0;
+}
+
+static struct i2c_device_id isl29020_id[] = {
+ { "isl29020", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, isl29020_id);
+
+#ifdef CONFIG_PM
+
+static int isl29020_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ als_set_power_state(client, 0);
+ return 0;
+}
+
+static int isl29020_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ als_set_power_state(client, 1);
+ return 0;
+}
+
+static const struct dev_pm_ops isl29020_pm_ops = {
+ .runtime_suspend = isl29020_runtime_suspend,
+ .runtime_resume = isl29020_runtime_resume,
+};
+
+#define ISL29020_PM_OPS (&isl29020_pm_ops)
+#else /* CONFIG_PM */
+#define ISL29020_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_driver isl29020_driver = {
+ .driver = {
+ .name = "isl29020",
+ .pm = ISL29020_PM_OPS,
+ },
+ .probe = isl29020_probe,
+ .remove = isl29020_remove,
+ .id_table = isl29020_id,
+};
+
+static int __init sensor_isl29020_init(void)
+{
+ return i2c_add_driver(&isl29020_driver);
+}
+
+static void __exit sensor_isl29020_exit(void)
+{
+ i2c_del_driver(&isl29020_driver);
+}
+
+module_init(sensor_isl29020_init);
+module_exit(sensor_isl29020_exit);
+
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
+MODULE_DESCRIPTION("Intersil isl29020 ALS Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c
index 343b5d8ea697..81d7fa4ec0db 100644
--- a/drivers/misc/lkdtm.c
+++ b/drivers/misc/lkdtm.c
@@ -52,32 +52,32 @@
#define REC_NUM_DEFAULT 10
enum cname {
- INVALID,
- INT_HARDWARE_ENTRY,
- INT_HW_IRQ_EN,
- INT_TASKLET_ENTRY,
- FS_DEVRW,
- MEM_SWAPOUT,
- TIMERADD,
- SCSI_DISPATCH_CMD,
- IDE_CORE_CP,
- DIRECT,
+ CN_INVALID,
+ CN_INT_HARDWARE_ENTRY,
+ CN_INT_HW_IRQ_EN,
+ CN_INT_TASKLET_ENTRY,
+ CN_FS_DEVRW,
+ CN_MEM_SWAPOUT,
+ CN_TIMERADD,
+ CN_SCSI_DISPATCH_CMD,
+ CN_IDE_CORE_CP,
+ CN_DIRECT,
};
enum ctype {
- NONE,
- PANIC,
- BUG,
- EXCEPTION,
- LOOP,
- OVERFLOW,
- CORRUPT_STACK,
- UNALIGNED_LOAD_STORE_WRITE,
- OVERWRITE_ALLOCATION,
- WRITE_AFTER_FREE,
- SOFTLOCKUP,
- HARDLOCKUP,
- HUNG_TASK,
+ CT_NONE,
+ CT_PANIC,
+ CT_BUG,
+ CT_EXCEPTION,
+ CT_LOOP,
+ CT_OVERFLOW,
+ CT_CORRUPT_STACK,
+ CT_UNALIGNED_LOAD_STORE_WRITE,
+ CT_OVERWRITE_ALLOCATION,
+ CT_WRITE_AFTER_FREE,
+ CT_SOFTLOCKUP,
+ CT_HARDLOCKUP,
+ CT_HUNG_TASK,
};
static char* cp_name[] = {
@@ -117,8 +117,8 @@ static char* cpoint_type;
static int cpoint_count = DEFAULT_COUNT;
static int recur_count = REC_NUM_DEFAULT;
-static enum cname cpoint = INVALID;
-static enum ctype cptype = NONE;
+static enum cname cpoint = CN_INVALID;
+static enum ctype cptype = CT_NONE;
static int count = DEFAULT_COUNT;
module_param(recur_count, int, 0644);
@@ -207,12 +207,12 @@ static enum ctype parse_cp_type(const char *what, size_t count)
return i + 1;
}
- return NONE;
+ return CT_NONE;
}
static const char *cp_type_to_str(enum ctype type)
{
- if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type))
+ if (type == CT_NONE || type < 0 || type > ARRAY_SIZE(cp_type))
return "None";
return cp_type[type - 1];
@@ -220,7 +220,7 @@ static const char *cp_type_to_str(enum ctype type)
static const char *cp_name_to_str(enum cname name)
{
- if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
+ if (name == CN_INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
return "INVALID";
return cp_name[name - 1];
@@ -245,7 +245,7 @@ static int lkdtm_parse_commandline(void)
return -EINVAL;
cptype = parse_cp_type(cpoint_type, strlen(cpoint_type));
- if (cptype == NONE)
+ if (cptype == CT_NONE)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
@@ -274,30 +274,30 @@ static int recursive_loop(int a)
static void lkdtm_do_action(enum ctype which)
{
switch (which) {
- case PANIC:
+ case CT_PANIC:
panic("dumptest");
break;
- case BUG:
+ case CT_BUG:
BUG();
break;
- case EXCEPTION:
+ case CT_EXCEPTION:
*((int *) 0) = 0;
break;
- case LOOP:
+ case CT_LOOP:
for (;;)
;
break;
- case OVERFLOW:
+ case CT_OVERFLOW:
(void) recursive_loop(0);
break;
- case CORRUPT_STACK: {
+ case CT_CORRUPT_STACK: {
volatile u32 data[8];
volatile u32 *p = data;
p[12] = 0x12345678;
break;
}
- case UNALIGNED_LOAD_STORE_WRITE: {
+ case CT_UNALIGNED_LOAD_STORE_WRITE: {
static u8 data[5] __attribute__((aligned(4))) = {1, 2,
3, 4, 5};
u32 *p;
@@ -309,7 +309,7 @@ static void lkdtm_do_action(enum ctype which)
*p = val;
break;
}
- case OVERWRITE_ALLOCATION: {
+ case CT_OVERWRITE_ALLOCATION: {
size_t len = 1020;
u32 *data = kmalloc(len, GFP_KERNEL);
@@ -317,7 +317,7 @@ static void lkdtm_do_action(enum ctype which)
kfree(data);
break;
}
- case WRITE_AFTER_FREE: {
+ case CT_WRITE_AFTER_FREE: {
size_t len = 1024;
u32 *data = kmalloc(len, GFP_KERNEL);
@@ -326,21 +326,21 @@ static void lkdtm_do_action(enum ctype which)
memset(data, 0x78, len);
break;
}
- case SOFTLOCKUP:
+ case CT_SOFTLOCKUP:
preempt_disable();
for (;;)
cpu_relax();
break;
- case HARDLOCKUP:
+ case CT_HARDLOCKUP:
local_irq_disable();
for (;;)
cpu_relax();
break;
- case HUNG_TASK:
+ case CT_HUNG_TASK:
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
break;
- case NONE:
+ case CT_NONE:
default:
break;
}
@@ -363,43 +363,43 @@ static int lkdtm_register_cpoint(enum cname which)
{
int ret;
- cpoint = INVALID;
+ cpoint = CN_INVALID;
if (lkdtm.entry != NULL)
unregister_jprobe(&lkdtm);
switch (which) {
- case DIRECT:
+ case CN_DIRECT:
lkdtm_do_action(cptype);
return 0;
- case INT_HARDWARE_ENTRY:
+ case CN_INT_HARDWARE_ENTRY:
lkdtm.kp.symbol_name = "do_IRQ";
lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
break;
- case INT_HW_IRQ_EN:
+ case CN_INT_HW_IRQ_EN:
lkdtm.kp.symbol_name = "handle_IRQ_event";
lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event;
break;
- case INT_TASKLET_ENTRY:
+ case CN_INT_TASKLET_ENTRY:
lkdtm.kp.symbol_name = "tasklet_action";
lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action;
break;
- case FS_DEVRW:
+ case CN_FS_DEVRW:
lkdtm.kp.symbol_name = "ll_rw_block";
lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block;
break;
- case MEM_SWAPOUT:
+ case CN_MEM_SWAPOUT:
lkdtm.kp.symbol_name = "shrink_inactive_list";
lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
break;
- case TIMERADD:
+ case CN_TIMERADD:
lkdtm.kp.symbol_name = "hrtimer_start";
lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start;
break;
- case SCSI_DISPATCH_CMD:
+ case CN_SCSI_DISPATCH_CMD:
lkdtm.kp.symbol_name = "scsi_dispatch_cmd";
lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
break;
- case IDE_CORE_CP:
+ case CN_IDE_CORE_CP:
#ifdef CONFIG_IDE
lkdtm.kp.symbol_name = "generic_ide_ioctl";
lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
@@ -416,7 +416,7 @@ static int lkdtm_register_cpoint(enum cname which)
cpoint = which;
if ((ret = register_jprobe(&lkdtm)) < 0) {
printk(KERN_INFO "lkdtm: Couldn't register jprobe\n");
- cpoint = INVALID;
+ cpoint = CN_INVALID;
}
return ret;
@@ -445,7 +445,7 @@ static ssize_t do_register_entry(enum cname which, struct file *f,
cptype = parse_cp_type(buf, count);
free_page((unsigned long) buf);
- if (cptype == NONE)
+ if (cptype == CT_NONE)
return -EINVAL;
err = lkdtm_register_cpoint(which);
@@ -487,49 +487,49 @@ static int lkdtm_debugfs_open(struct inode *inode, struct file *file)
static ssize_t int_hardware_entry(struct file *f, const char __user *buf,
size_t count, loff_t *off)
{
- return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off);
+ return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
}
static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
size_t count, loff_t *off)
{
- return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off);
+ return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off);
}
static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
size_t count, loff_t *off)
{
- return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off);
+ return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off);
}
static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
size_t count, loff_t *off)
{
- return do_register_entry(FS_DEVRW, f, buf, count, off);
+ return do_register_entry(CN_FS_DEVRW, f, buf, count, off);
}
static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
size_t count, loff_t *off)
{
- return do_register_entry(MEM_SWAPOUT, f, buf, count, off);
+ return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off);
}
static ssize_t timeradd_entry(struct file *f, const char __user *buf,
size_t count, loff_t *off)
{
- return do_register_entry(TIMERADD, f, buf, count, off);
+ return do_register_entry(CN_TIMERADD, f, buf, count, off);
}
static ssize_t scsi_dispatch_cmd_entry(struct file *f,
const char __user *buf, size_t count, loff_t *off)
{
- return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off);
+ return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
}
static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
size_t count, loff_t *off)
{
- return do_register_entry(IDE_CORE_CP, f, buf, count, off);
+ return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
}
/* Special entry to just crash directly. Available without KPROBEs */
@@ -557,7 +557,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
type = parse_cp_type(buf, count);
free_page((unsigned long) buf);
- if (type == NONE)
+ if (type == CT_NONE)
return -EINVAL;
printk(KERN_INFO "lkdtm: Performing direct entry %s\n",
@@ -649,7 +649,7 @@ static int __init lkdtm_module_init(void)
goto out_err;
}
- if (cpoint != INVALID && cptype != NONE) {
+ if (cpoint != CN_INVALID && cptype != CT_NONE) {
ret = lkdtm_register_cpoint(cpoint);
if (ret < 0) {
printk(KERN_INFO "lkdtm: Invalid crash point %d\n",
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index 4197a3cb26ba..b05db55c8c8e 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -343,8 +343,10 @@ static int __devinit phantom_probe(struct pci_dev *pdev,
int retval;
retval = pci_enable_device(pdev);
- if (retval)
+ if (retval) {
+ dev_err(&pdev->dev, "pci_enable_device failed!\n");
goto err;
+ }
minor = phantom_get_free();
if (minor == PHANTOM_MAX_MINORS) {
@@ -356,8 +358,10 @@ static int __devinit phantom_probe(struct pci_dev *pdev,
phantom_devices[minor] = 1;
retval = pci_request_regions(pdev, "phantom");
- if (retval)
+ if (retval) {
+ dev_err(&pdev->dev, "pci_request_regions failed!\n");
goto err_null;
+ }
retval = -ENOMEM;
pht = kzalloc(sizeof(*pht), GFP_KERNEL);
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
index 1f59ee2226ca..17bbacb1b4b1 100644
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ b/drivers/misc/sgi-xp/xpc_uv.c
@@ -417,6 +417,7 @@ xpc_process_activate_IRQ_rcvd_uv(void)
static void
xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
struct xpc_activate_mq_msghdr_uv *msg_hdr,
+ int part_setup,
int *wakeup_hb_checker)
{
unsigned long irq_flags;
@@ -481,6 +482,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREQUEST_UV: {
struct xpc_activate_mq_msg_chctl_closerequest_uv *msg;
+ if (!part_setup)
+ break;
+
msg = container_of(msg_hdr, struct
xpc_activate_mq_msg_chctl_closerequest_uv,
hdr);
@@ -497,6 +501,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
case XPC_ACTIVATE_MQ_MSG_CHCTL_CLOSEREPLY_UV: {
struct xpc_activate_mq_msg_chctl_closereply_uv *msg;
+ if (!part_setup)
+ break;
+
msg = container_of(msg_hdr, struct
xpc_activate_mq_msg_chctl_closereply_uv,
hdr);
@@ -511,6 +518,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREQUEST_UV: {
struct xpc_activate_mq_msg_chctl_openrequest_uv *msg;
+ if (!part_setup)
+ break;
+
msg = container_of(msg_hdr, struct
xpc_activate_mq_msg_chctl_openrequest_uv,
hdr);
@@ -528,6 +538,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENREPLY_UV: {
struct xpc_activate_mq_msg_chctl_openreply_uv *msg;
+ if (!part_setup)
+ break;
+
msg = container_of(msg_hdr, struct
xpc_activate_mq_msg_chctl_openreply_uv, hdr);
args = &part->remote_openclose_args[msg->ch_number];
@@ -545,6 +558,9 @@ xpc_handle_activate_mq_msg_uv(struct xpc_partition *part,
case XPC_ACTIVATE_MQ_MSG_CHCTL_OPENCOMPLETE_UV: {
struct xpc_activate_mq_msg_chctl_opencomplete_uv *msg;
+ if (!part_setup)
+ break;
+
msg = container_of(msg_hdr, struct
xpc_activate_mq_msg_chctl_opencomplete_uv, hdr);
spin_lock_irqsave(&part->chctl_lock, irq_flags);
@@ -621,6 +637,7 @@ xpc_handle_activate_IRQ_uv(int irq, void *dev_id)
part_referenced = xpc_part_ref(part);
xpc_handle_activate_mq_msg_uv(part, msg_hdr,
+ part_referenced,
&wakeup_hb_checker);
if (part_referenced)
xpc_part_deref(part);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 68d12794cfd9..1a0261160e56 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -237,7 +237,7 @@ endchoice
config MMC_ATMELMCI_DMA
bool "Atmel MCI DMA support (EXPERIMENTAL)"
- depends on MMC_ATMELMCI && AVR32 && DMA_ENGINE && EXPERIMENTAL
+ depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE && EXPERIMENTAL
help
Say Y here to have the Atmel MCI driver use a DMA engine to
do data transfers and thus increase the throughput and
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 4526d2791f29..4693e62145a6 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -364,6 +364,7 @@ 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:
@@ -396,6 +397,17 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
}
} else {
host->vcc = reg;
+ ocr_value = mmc_regulator_get_ocrmask(reg);
+ if (!mmc_slot(host).ocr_mask) {
+ 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);
+ mmc_slot(host).ocr_mask = 0;
+ return -EINVAL;
+ }
+ }
mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg);
/* Allow an aux regulator */
@@ -982,6 +994,17 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | bit);
+ /*
+ * OMAP4 ES2 and greater has an updated reset logic.
+ * Monitor a 0->1 transition first
+ */
+ if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {
+ while ((!(OMAP_HSMMC_READ(host, SYSCTL) & bit))
+ && (i++ < limit))
+ cpu_relax();
+ }
+ i = 0;
+
while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&
(i++ < limit))
cpu_relax();
@@ -2003,6 +2026,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if (res == NULL || irq < 0)
return -ENXIO;
+ res->start += pdata->reg_offset;
+ res->end += pdata->reg_offset;
res = request_mem_region(res->start, res->end - res->start + 1,
pdev->name);
if (res == NULL)
@@ -2116,23 +2141,9 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
- switch (mmc_slot(host).wires) {
- case 8:
- mmc->caps |= MMC_CAP_8_BIT_DATA;
- /* Fall through */
- case 4:
+ mmc->caps |= mmc_slot(host).caps;
+ if (mmc->caps & MMC_CAP_8_BIT_DATA)
mmc->caps |= MMC_CAP_4_BIT_DATA;
- break;
- case 1:
- /* Nothing to crib here */
- case 0:
- /* Assuming nothing was given by board, Core use's 1-Bit */
- break;
- default:
- /* Completely unexpected.. Core goes with 1-Bit Width */
- dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!"
- "using 1 instead\n", mmc_slot(host).wires);
- }
if (mmc_slot(host).nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 6f512b5c117b..ea22520c0406 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -924,6 +924,13 @@ static int __devinit m25p_probe(struct spi_device *spi)
nr_parts = data->nr_parts;
}
+#ifdef CONFIG_OF
+ if (nr_parts <= 0 && spi->dev.of_node) {
+ nr_parts = of_mtd_parse_partitions(&spi->dev,
+ spi->dev.of_node, &parts);
+ }
+#endif
+
if (nr_parts > 0) {
for (i = 0; i < nr_parts; i++) {
DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index fe63f6bd663c..ec3edf6e68b4 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -294,7 +294,7 @@ static int __devinit of_flash_probe(struct platform_device *dev,
info->list[i].map.name = dev_name(&dev->dev);
info->list[i].map.phys = res.start;
info->list[i].map.size = res_size;
- info->list[i].map.bankwidth = *width;
+ info->list[i].map.bankwidth = be32_to_cpup(width);
err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys,
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index 2ac7367afe77..8beb0d0233b5 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -35,6 +35,7 @@
#include <linux/slab.h>
#include <mach/nand.h>
+#include <mach/aemif.h>
#include <asm/mach-types.h>
@@ -74,6 +75,8 @@ struct davinci_nand_info {
uint32_t mask_cle;
uint32_t core_chipsel;
+
+ struct davinci_aemif_timing *timing;
};
static DEFINE_SPINLOCK(davinci_nand_lock);
@@ -478,36 +481,6 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd)
return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0);
}
-static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info)
-{
- uint32_t regval, a1cr;
-
- /*
- * NAND FLASH timings @ PLL1 == 459 MHz
- * - AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz
- * - AEMIF.CLK period = 1/76.5 MHz = 13.1 ns
- */
- regval = 0
- | (0 << 31) /* selectStrobe */
- | (0 << 30) /* extWait (never with NAND) */
- | (1 << 26) /* writeSetup 10 ns */
- | (3 << 20) /* writeStrobe 40 ns */
- | (1 << 17) /* writeHold 10 ns */
- | (0 << 13) /* readSetup 10 ns */
- | (3 << 7) /* readStrobe 60 ns */
- | (0 << 4) /* readHold 10 ns */
- | (3 << 2) /* turnAround ?? ns */
- | (0 << 0) /* asyncSize 8-bit bus */
- ;
- a1cr = davinci_nand_readl(info, A1CR_OFFSET);
- if (a1cr != regval) {
- dev_dbg(info->dev, "Warning: NAND config: Set A1CR " \
- "reg to 0x%08x, was 0x%08x, should be done by " \
- "bootloader.\n", regval, a1cr);
- davinci_nand_writel(info, A1CR_OFFSET, regval);
- }
-}
-
/*----------------------------------------------------------------------*/
/* An ECC layout for using 4-bit ECC with small-page flash, storing
@@ -611,6 +584,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->chip.options = pdata->options;
info->chip.bbt_td = pdata->bbt_td;
info->chip.bbt_md = pdata->bbt_md;
+ info->timing = pdata->timing;
info->ioaddr = (uint32_t __force) vaddr;
@@ -688,15 +662,25 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
goto err_clk_enable;
}
- /* EMIF timings should normally be set by the boot loader,
- * especially after boot-from-NAND. The *only* reason to
- * have this special casing for the DM6446 EVM is to work
- * with boot-from-NOR ... with CS0 manually re-jumpered
- * (after startup) so it addresses the NAND flash, not NOR.
- * Even for dev boards, that's unusually rude...
+ /*
+ * Setup Async configuration register in case we did not boot from
+ * NAND and so bootloader did not bother to set it up.
*/
- if (machine_is_davinci_evm())
- nand_dm6446evm_flash_init(info);
+ val = davinci_nand_readl(info, A1CR_OFFSET + info->core_chipsel * 4);
+
+ /* Extended Wait is not valid and Select Strobe mode is not used */
+ val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK);
+ if (info->chip.options & NAND_BUSWIDTH_16)
+ val |= 0x1;
+
+ davinci_nand_writel(info, A1CR_OFFSET + info->core_chipsel * 4, val);
+
+ ret = davinci_aemif_setup_timing(info->timing, info->base,
+ info->core_chipsel);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "NAND timing values setup fail\n");
+ goto err_timing;
+ }
spin_lock_irq(&davinci_nand_lock);
@@ -809,6 +793,7 @@ syndrome_done:
return 0;
err_scan:
+err_timing:
clk_disable(info->clk);
err_clk_enable:
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index 8bf7dc6d1ce6..7bd171eefd21 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -53,8 +53,8 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
continue;
}
- (*pparts)[i].offset = reg[0];
- (*pparts)[i].size = reg[1];
+ (*pparts)[i].offset = be32_to_cpu(reg[0]);
+ (*pparts)[i].size = be32_to_cpu(reg[1]);
partname = of_get_property(pp, "label", &len);
if (!partname)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 77c1fab7d774..86fe67fd49ba 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -883,14 +883,6 @@ config BFIN_RX_DESC_NUM
help
Set the number of buffer packets used in driver.
-config BFIN_MAC_RMII
- bool "RMII PHY Interface"
- depends on BFIN_MAC
- default y if BFIN527_EZKIT
- default n if BFIN537_STAMP
- help
- Use Reduced PHY MII Interface
-
config BFIN_MAC_USE_HWSTAMP
bool "Use IEEE 1588 hwstamp"
depends on BFIN_MAC && BF518
@@ -954,6 +946,8 @@ config NET_NETX
config TI_DAVINCI_EMAC
tristate "TI DaVinci EMAC Support"
depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+ select TI_DAVINCI_MDIO
+ select TI_DAVINCI_CPDMA
select PHYLIB
help
This driver supports TI's DaVinci Ethernet .
@@ -961,6 +955,25 @@ config TI_DAVINCI_EMAC
To compile this driver as a module, choose M here: the module
will be called davinci_emac_driver. This is recommended.
+config TI_DAVINCI_MDIO
+ tristate "TI DaVinci MDIO Support"
+ depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+ select PHYLIB
+ help
+ This driver supports TI's DaVinci MDIO module.
+
+ To compile this driver as a module, choose M here: the module
+ will be called davinci_mdio. This is recommended.
+
+config TI_DAVINCI_CPDMA
+ tristate "TI DaVinci CPDMA Support"
+ depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+ help
+ This driver supports TI's DaVinci CPDMA dma engine.
+
+ To compile this driver as a module, choose M here: the module
+ will be called davinci_cpdma. This is recommended.
+
config DM9000
tristate "DM9000 support"
depends on ARM || BLACKFIN || MIPS
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b8bf93d4a132..652fc6b98039 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -7,6 +7,8 @@ obj-$(CONFIG_MDIO) += mdio.o
obj-$(CONFIG_PHYLIB) += phy/
obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
+obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
+obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_E1000E) += e1000e/
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index f7233191162b..ce1e5e9d06f6 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -1,7 +1,7 @@
/*
* Blackfin On-Chip MAC Driver
*
- * Copyright 2004-2007 Analog Devices Inc.
+ * Copyright 2004-2010 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
@@ -23,7 +23,6 @@
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/mii.h>
-#include <linux/phy.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
@@ -76,12 +75,6 @@ static struct net_dma_desc_tx *current_tx_ptr;
static struct net_dma_desc_tx *tx_desc;
static struct net_dma_desc_rx *rx_desc;
-#if defined(CONFIG_BFIN_MAC_RMII)
-static u16 pin_req[] = P_RMII0;
-#else
-static u16 pin_req[] = P_MII0;
-#endif
-
static void desc_list_free(void)
{
struct net_dma_desc_rx *r;
@@ -347,23 +340,23 @@ static void bfin_mac_adjust_link(struct net_device *dev)
}
if (phydev->speed != lp->old_speed) {
-#if defined(CONFIG_BFIN_MAC_RMII)
- u32 opmode = bfin_read_EMAC_OPMODE();
- switch (phydev->speed) {
- case 10:
- opmode |= RMII_10;
- break;
- case 100:
- opmode &= ~(RMII_10);
- break;
- default:
- printk(KERN_WARNING
- "%s: Ack! Speed (%d) is not 10/100!\n",
- DRV_NAME, phydev->speed);
- break;
+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) {
+ u32 opmode = bfin_read_EMAC_OPMODE();
+ switch (phydev->speed) {
+ case 10:
+ opmode |= RMII_10;
+ break;
+ case 100:
+ opmode &= ~RMII_10;
+ break;
+ default:
+ printk(KERN_WARNING
+ "%s: Ack! Speed (%d) is not 10/100!\n",
+ DRV_NAME, phydev->speed);
+ break;
+ }
+ bfin_write_EMAC_OPMODE(opmode);
}
- bfin_write_EMAC_OPMODE(opmode);
-#endif
new_state = 1;
lp->old_speed = phydev->speed;
@@ -392,7 +385,7 @@ static void bfin_mac_adjust_link(struct net_device *dev)
/* MDC = 2.5 MHz */
#define MDC_CLK 2500000
-static int mii_probe(struct net_device *dev)
+static int mii_probe(struct net_device *dev, int phy_mode)
{
struct bfin_mac_local *lp = netdev_priv(dev);
struct phy_device *phydev = NULL;
@@ -411,8 +404,8 @@ static int mii_probe(struct net_device *dev)
sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div);
bfin_write_EMAC_SYSCTL(sysctl);
- /* search for connect PHY device */
- for (i = 0; i < PHY_MAX_ADDR; i++) {
+ /* search for connected PHY device */
+ for (i = 0; i < PHY_MAX_ADDR; ++i) {
struct phy_device *const tmp_phydev = lp->mii_bus->phy_map[i];
if (!tmp_phydev)
@@ -429,13 +422,14 @@ static int mii_probe(struct net_device *dev)
return -ENODEV;
}
-#if defined(CONFIG_BFIN_MAC_RMII)
- phydev = phy_connect(dev, dev_name(&phydev->dev), &bfin_mac_adjust_link,
- 0, PHY_INTERFACE_MODE_RMII);
-#else
+ if (phy_mode != PHY_INTERFACE_MODE_RMII &&
+ phy_mode != PHY_INTERFACE_MODE_MII) {
+ printk(KERN_INFO "%s: Invalid phy interface mode\n", dev->name);
+ return -EINVAL;
+ }
+
phydev = phy_connect(dev, dev_name(&phydev->dev), &bfin_mac_adjust_link,
- 0, PHY_INTERFACE_MODE_MII);
-#endif
+ 0, phy_mode);
if (IS_ERR(phydev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
@@ -570,6 +564,8 @@ static const struct ethtool_ops bfin_mac_ethtool_ops = {
/**************************************************************************/
void setup_system_regs(struct net_device *dev)
{
+ struct bfin_mac_local *lp = netdev_priv(dev);
+ int i;
unsigned short sysctl;
/*
@@ -577,6 +573,15 @@ void setup_system_regs(struct net_device *dev)
* Configure checksum support and rcve frame word alignment
*/
sysctl = bfin_read_EMAC_SYSCTL();
+ /*
+ * check if interrupt is requested for any PHY,
+ * enable PHY interrupt only if needed
+ */
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ if (lp->mii_bus->irq[i] != PHY_POLL)
+ break;
+ if (i < PHY_MAX_ADDR)
+ sysctl |= PHYIE;
sysctl |= RXDWA;
#if defined(BFIN_MAC_CSUM_OFFLOAD)
sysctl |= RXCKS;
@@ -1203,7 +1208,7 @@ static void bfin_mac_disable(void)
/*
* Enable Interrupts, Receive, and Transmit
*/
-static int bfin_mac_enable(void)
+static int bfin_mac_enable(struct phy_device *phydev)
{
int ret;
u32 opmode;
@@ -1233,12 +1238,13 @@ static int bfin_mac_enable(void)
opmode |= DRO | DC | PSF;
opmode |= RE;
-#if defined(CONFIG_BFIN_MAC_RMII)
- opmode |= RMII; /* For Now only 100MBit are supported */
+ if (phydev->interface == PHY_INTERFACE_MODE_RMII) {
+ opmode |= RMII; /* For Now only 100MBit are supported */
#if (defined(CONFIG_BF537) || defined(CONFIG_BF536)) && CONFIG_BF_REV_0_2
- opmode |= TE;
-#endif
+ opmode |= TE;
#endif
+ }
+
/* Turn on the EMAC rx */
bfin_write_EMAC_OPMODE(opmode);
@@ -1270,7 +1276,7 @@ static void bfin_mac_timeout(struct net_device *dev)
if (netif_queue_stopped(lp->ndev))
netif_wake_queue(lp->ndev);
- bfin_mac_enable();
+ bfin_mac_enable(lp->phydev);
/* We can accept TX packets again */
dev->trans_start = jiffies; /* prevent tx timeout */
@@ -1342,11 +1348,19 @@ static void bfin_mac_set_multicast_list(struct net_device *dev)
static int bfin_mac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
+ struct bfin_mac_local *lp = netdev_priv(netdev);
+
+ if (!netif_running(netdev))
+ return -EINVAL;
+
switch (cmd) {
case SIOCSHWTSTAMP:
return bfin_mac_hwtstamp_ioctl(netdev, ifr, cmd);
default:
- return -EOPNOTSUPP;
+ if (lp->phydev)
+ return phy_mii_ioctl(lp->phydev, ifr, cmd);
+ else
+ return -EOPNOTSUPP;
}
}
@@ -1394,7 +1408,7 @@ static int bfin_mac_open(struct net_device *dev)
setup_mac_addr(dev->dev_addr);
bfin_mac_disable();
- ret = bfin_mac_enable();
+ ret = bfin_mac_enable(lp->phydev);
if (ret)
return ret;
pr_debug("hardware init finished\n");
@@ -1450,6 +1464,7 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev)
struct net_device *ndev;
struct bfin_mac_local *lp;
struct platform_device *pd;
+ struct bfin_mii_bus_platform_data *mii_bus_data;
int rc;
ndev = alloc_etherdev(sizeof(struct bfin_mac_local));
@@ -1501,11 +1516,12 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev)
if (!lp->mii_bus) {
dev_err(&pdev->dev, "Cannot get mii_bus!\n");
rc = -ENODEV;
- goto out_err_mii_bus_probe;
+ goto out_err_probe_mac;
}
lp->mii_bus->priv = ndev;
+ mii_bus_data = pd->dev.platform_data;
- rc = mii_probe(ndev);
+ rc = mii_probe(ndev, mii_bus_data->phy_mode);
if (rc) {
dev_err(&pdev->dev, "MII Probe failed!\n");
goto out_err_mii_probe;
@@ -1552,8 +1568,6 @@ out_err_request_irq:
out_err_mii_probe:
mdiobus_unregister(lp->mii_bus);
mdiobus_free(lp->mii_bus);
-out_err_mii_bus_probe:
- peripheral_free_list(pin_req);
out_err_probe_mac:
platform_set_drvdata(pdev, NULL);
free_netdev(ndev);
@@ -1576,8 +1590,6 @@ static int __devexit bfin_mac_remove(struct platform_device *pdev)
free_netdev(ndev);
- peripheral_free_list(pin_req);
-
return 0;
}
@@ -1623,12 +1635,21 @@ static int bfin_mac_resume(struct platform_device *pdev)
static int __devinit bfin_mii_bus_probe(struct platform_device *pdev)
{
struct mii_bus *miibus;
+ struct bfin_mii_bus_platform_data *mii_bus_pd;
+ const unsigned short *pin_req;
int rc, i;
+ mii_bus_pd = dev_get_platdata(&pdev->dev);
+ if (!mii_bus_pd) {
+ dev_err(&pdev->dev, "No peripherals in platform data!\n");
+ return -EINVAL;
+ }
+
/*
* We are setting up a network card,
* so set the GPIO pins to Ethernet mode
*/
+ pin_req = mii_bus_pd->mac_peripherals;
rc = peripheral_request_list(pin_req, DRV_NAME);
if (rc) {
dev_err(&pdev->dev, "Requesting peripherals failed!\n");
@@ -1645,13 +1666,30 @@ static int __devinit bfin_mii_bus_probe(struct platform_device *pdev)
miibus->parent = &pdev->dev;
miibus->name = "bfin_mii_bus";
+ miibus->phy_mask = mii_bus_pd->phy_mask;
+
snprintf(miibus->id, MII_BUS_ID_SIZE, "0");
miibus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (miibus->irq == NULL)
- goto out_err_alloc;
- for (i = 0; i < PHY_MAX_ADDR; ++i)
+ if (!miibus->irq)
+ goto out_err_irq_alloc;
+
+ for (i = rc; i < PHY_MAX_ADDR; ++i)
miibus->irq[i] = PHY_POLL;
+ rc = clamp(mii_bus_pd->phydev_number, 0, PHY_MAX_ADDR);
+ if (rc != mii_bus_pd->phydev_number)
+ dev_err(&pdev->dev, "Invalid number (%i) of phydevs\n",
+ mii_bus_pd->phydev_number);
+ for (i = 0; i < rc; ++i) {
+ unsigned short phyaddr = mii_bus_pd->phydev_data[i].addr;
+ if (phyaddr < PHY_MAX_ADDR)
+ miibus->irq[phyaddr] = mii_bus_pd->phydev_data[i].irq;
+ else
+ dev_err(&pdev->dev,
+ "Invalid PHY address %i for phydev %i\n",
+ phyaddr, i);
+ }
+
rc = mdiobus_register(miibus);
if (rc) {
dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
@@ -1663,6 +1701,7 @@ static int __devinit bfin_mii_bus_probe(struct platform_device *pdev)
out_err_mdiobus_register:
kfree(miibus->irq);
+out_err_irq_alloc:
mdiobus_free(miibus);
out_err_alloc:
peripheral_free_list(pin_req);
@@ -1673,11 +1712,15 @@ out_err_alloc:
static int __devexit bfin_mii_bus_remove(struct platform_device *pdev)
{
struct mii_bus *miibus = platform_get_drvdata(pdev);
+ struct bfin_mii_bus_platform_data *mii_bus_pd =
+ dev_get_platdata(&pdev->dev);
+
platform_set_drvdata(pdev, NULL);
mdiobus_unregister(miibus);
kfree(miibus->irq);
mdiobus_free(miibus);
- peripheral_free_list(pin_req);
+ peripheral_free_list(mii_bus_pd->mac_peripherals);
+
return 0;
}
diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h
index 04e4050df18b..aed68bed2365 100644
--- a/drivers/net/bfin_mac.h
+++ b/drivers/net/bfin_mac.h
@@ -14,6 +14,8 @@
#include <linux/clocksource.h>
#include <linux/timecompare.h>
#include <linux/timer.h>
+#include <linux/etherdevice.h>
+#include <linux/bfin_mac.h>
#define BFIN_MAC_CSUM_OFFLOAD
diff --git a/drivers/net/davinci_cpdma.c b/drivers/net/davinci_cpdma.c
new file mode 100644
index 000000000000..e92b2b6cd8c4
--- /dev/null
+++ b/drivers/net/davinci_cpdma.c
@@ -0,0 +1,965 @@
+/*
+ * Texas Instruments CPDMA Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation 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/spinlock.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+
+#include "davinci_cpdma.h"
+
+/* DMA Registers */
+#define CPDMA_TXIDVER 0x00
+#define CPDMA_TXCONTROL 0x04
+#define CPDMA_TXTEARDOWN 0x08
+#define CPDMA_RXIDVER 0x10
+#define CPDMA_RXCONTROL 0x14
+#define CPDMA_SOFTRESET 0x1c
+#define CPDMA_RXTEARDOWN 0x18
+#define CPDMA_TXINTSTATRAW 0x80
+#define CPDMA_TXINTSTATMASKED 0x84
+#define CPDMA_TXINTMASKSET 0x88
+#define CPDMA_TXINTMASKCLEAR 0x8c
+#define CPDMA_MACINVECTOR 0x90
+#define CPDMA_MACEOIVECTOR 0x94
+#define CPDMA_RXINTSTATRAW 0xa0
+#define CPDMA_RXINTSTATMASKED 0xa4
+#define CPDMA_RXINTMASKSET 0xa8
+#define CPDMA_RXINTMASKCLEAR 0xac
+#define CPDMA_DMAINTSTATRAW 0xb0
+#define CPDMA_DMAINTSTATMASKED 0xb4
+#define CPDMA_DMAINTMASKSET 0xb8
+#define CPDMA_DMAINTMASKCLEAR 0xbc
+#define CPDMA_DMAINT_HOSTERR BIT(1)
+
+/* the following exist only if has_ext_regs is set */
+#define CPDMA_DMACONTROL 0x20
+#define CPDMA_DMASTATUS 0x24
+#define CPDMA_RXBUFFOFS 0x28
+#define CPDMA_EM_CONTROL 0x2c
+
+/* Descriptor mode bits */
+#define CPDMA_DESC_SOP BIT(31)
+#define CPDMA_DESC_EOP BIT(30)
+#define CPDMA_DESC_OWNER BIT(29)
+#define CPDMA_DESC_EOQ BIT(28)
+#define CPDMA_DESC_TD_COMPLETE BIT(27)
+#define CPDMA_DESC_PASS_CRC BIT(26)
+
+#define CPDMA_TEARDOWN_VALUE 0xfffffffc
+
+struct cpdma_desc {
+ /* hardware fields */
+ u32 hw_next;
+ u32 hw_buffer;
+ u32 hw_len;
+ u32 hw_mode;
+ /* software fields */
+ void *sw_token;
+ u32 sw_buffer;
+ u32 sw_len;
+};
+
+struct cpdma_desc_pool {
+ u32 phys;
+ void __iomem *iomap; /* ioremap map */
+ void *cpumap; /* dma_alloc map */
+ int desc_size, mem_size;
+ int num_desc, used_desc;
+ unsigned long *bitmap;
+ struct device *dev;
+ spinlock_t lock;
+};
+
+enum cpdma_state {
+ CPDMA_STATE_IDLE,
+ CPDMA_STATE_ACTIVE,
+ CPDMA_STATE_TEARDOWN,
+};
+
+const char *cpdma_state_str[] = { "idle", "active", "teardown" };
+
+struct cpdma_ctlr {
+ enum cpdma_state state;
+ struct cpdma_params params;
+ struct device *dev;
+ struct cpdma_desc_pool *pool;
+ spinlock_t lock;
+ struct cpdma_chan *channels[2 * CPDMA_MAX_CHANNELS];
+};
+
+struct cpdma_chan {
+ enum cpdma_state state;
+ struct cpdma_ctlr *ctlr;
+ int chan_num;
+ spinlock_t lock;
+ struct cpdma_desc __iomem *head, *tail;
+ int count;
+ void __iomem *hdp, *cp, *rxfree;
+ u32 mask;
+ cpdma_handler_fn handler;
+ enum dma_data_direction dir;
+ struct cpdma_chan_stats stats;
+ /* offsets into dmaregs */
+ int int_set, int_clear, td;
+};
+
+/* The following make access to common cpdma_ctlr params more readable */
+#define dmaregs params.dmaregs
+#define num_chan params.num_chan
+
+/* various accessors */
+#define dma_reg_read(ctlr, ofs) __raw_readl((ctlr)->dmaregs + (ofs))
+#define chan_read(chan, fld) __raw_readl((chan)->fld)
+#define desc_read(desc, fld) __raw_readl(&(desc)->fld)
+#define dma_reg_write(ctlr, ofs, v) __raw_writel(v, (ctlr)->dmaregs + (ofs))
+#define chan_write(chan, fld, v) __raw_writel(v, (chan)->fld)
+#define desc_write(desc, fld, v) __raw_writel((u32)(v), &(desc)->fld)
+
+/*
+ * Utility constructs for a cpdma descriptor pool. Some devices (e.g. davinci
+ * emac) have dedicated on-chip memory for these descriptors. Some other
+ * devices (e.g. cpsw switches) use plain old memory. Descriptor pools
+ * abstract out these details
+ */
+static struct cpdma_desc_pool *
+cpdma_desc_pool_create(struct device *dev, u32 phys, int size, int align)
+{
+ int bitmap_size;
+ struct cpdma_desc_pool *pool;
+
+ pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool)
+ return NULL;
+
+ spin_lock_init(&pool->lock);
+
+ pool->dev = dev;
+ pool->mem_size = size;
+ pool->desc_size = ALIGN(sizeof(struct cpdma_desc), align);
+ pool->num_desc = size / pool->desc_size;
+
+ bitmap_size = (pool->num_desc / BITS_PER_LONG) * sizeof(long);
+ pool->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!pool->bitmap)
+ goto fail;
+
+ if (phys) {
+ pool->phys = phys;
+ pool->iomap = ioremap(phys, size);
+ } else {
+ pool->cpumap = dma_alloc_coherent(dev, size, &pool->phys,
+ GFP_KERNEL);
+ pool->iomap = (void __force __iomem *)pool->cpumap;
+ }
+
+ if (pool->iomap)
+ return pool;
+
+fail:
+ kfree(pool->bitmap);
+ kfree(pool);
+ return NULL;
+}
+
+static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
+{
+ unsigned long flags;
+
+ if (!pool)
+ return;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ WARN_ON(pool->used_desc);
+ kfree(pool->bitmap);
+ if (pool->cpumap) {
+ dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap,
+ pool->phys);
+ } else {
+ iounmap(pool->iomap);
+ }
+ spin_unlock_irqrestore(&pool->lock, flags);
+ kfree(pool);
+}
+
+static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool,
+ struct cpdma_desc __iomem *desc)
+{
+ if (!desc)
+ return 0;
+ return pool->phys + (__force dma_addr_t)desc -
+ (__force dma_addr_t)pool->iomap;
+}
+
+static inline struct cpdma_desc __iomem *
+desc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma)
+{
+ return dma ? pool->iomap + dma - pool->phys : NULL;
+}
+
+static struct cpdma_desc __iomem *
+cpdma_desc_alloc(struct cpdma_desc_pool *pool, int num_desc)
+{
+ unsigned long flags;
+ int index;
+ struct cpdma_desc __iomem *desc = NULL;
+
+ spin_lock_irqsave(&pool->lock, flags);
+
+ index = bitmap_find_next_zero_area(pool->bitmap, pool->num_desc, 0,
+ num_desc, 0);
+ if (index < pool->num_desc) {
+ bitmap_set(pool->bitmap, index, num_desc);
+ desc = pool->iomap + pool->desc_size * index;
+ pool->used_desc++;
+ }
+
+ spin_unlock_irqrestore(&pool->lock, flags);
+ return desc;
+}
+
+static void cpdma_desc_free(struct cpdma_desc_pool *pool,
+ struct cpdma_desc __iomem *desc, int num_desc)
+{
+ unsigned long flags, index;
+
+ index = ((unsigned long)desc - (unsigned long)pool->iomap) /
+ pool->desc_size;
+ spin_lock_irqsave(&pool->lock, flags);
+ bitmap_clear(pool->bitmap, index, num_desc);
+ pool->used_desc--;
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
+{
+ struct cpdma_ctlr *ctlr;
+
+ ctlr = kzalloc(sizeof(*ctlr), GFP_KERNEL);
+ if (!ctlr)
+ return NULL;
+
+ ctlr->state = CPDMA_STATE_IDLE;
+ ctlr->params = *params;
+ ctlr->dev = params->dev;
+ spin_lock_init(&ctlr->lock);
+
+ ctlr->pool = cpdma_desc_pool_create(ctlr->dev,
+ ctlr->params.desc_mem_phys,
+ ctlr->params.desc_mem_size,
+ ctlr->params.desc_align);
+ if (!ctlr->pool) {
+ kfree(ctlr);
+ return NULL;
+ }
+
+ if (WARN_ON(ctlr->num_chan > CPDMA_MAX_CHANNELS))
+ ctlr->num_chan = CPDMA_MAX_CHANNELS;
+ return ctlr;
+}
+
+int cpdma_ctlr_start(struct cpdma_ctlr *ctlr)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ if (ctlr->state != CPDMA_STATE_IDLE) {
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return -EBUSY;
+ }
+
+ if (ctlr->params.has_soft_reset) {
+ unsigned long timeout = jiffies + HZ/10;
+
+ dma_reg_write(ctlr, CPDMA_SOFTRESET, 1);
+ while (time_before(jiffies, timeout)) {
+ if (dma_reg_read(ctlr, CPDMA_SOFTRESET) == 0)
+ break;
+ }
+ WARN_ON(!time_before(jiffies, timeout));
+ }
+
+ for (i = 0; i < ctlr->num_chan; i++) {
+ __raw_writel(0, ctlr->params.txhdp + 4 * i);
+ __raw_writel(0, ctlr->params.rxhdp + 4 * i);
+ __raw_writel(0, ctlr->params.txcp + 4 * i);
+ __raw_writel(0, ctlr->params.rxcp + 4 * i);
+ }
+
+ dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff);
+ dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff);
+
+ dma_reg_write(ctlr, CPDMA_TXCONTROL, 1);
+ dma_reg_write(ctlr, CPDMA_RXCONTROL, 1);
+
+ ctlr->state = CPDMA_STATE_ACTIVE;
+
+ for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+ if (ctlr->channels[i])
+ cpdma_chan_start(ctlr->channels[i]);
+ }
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return 0;
+}
+
+int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ if (ctlr->state != CPDMA_STATE_ACTIVE) {
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return -EINVAL;
+ }
+
+ ctlr->state = CPDMA_STATE_TEARDOWN;
+
+ for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+ if (ctlr->channels[i])
+ cpdma_chan_stop(ctlr->channels[i]);
+ }
+
+ dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff);
+ dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff);
+
+ dma_reg_write(ctlr, CPDMA_TXCONTROL, 0);
+ dma_reg_write(ctlr, CPDMA_RXCONTROL, 0);
+
+ ctlr->state = CPDMA_STATE_IDLE;
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return 0;
+}
+
+int cpdma_ctlr_dump(struct cpdma_ctlr *ctlr)
+{
+ struct device *dev = ctlr->dev;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ dev_info(dev, "CPDMA: state: %s", cpdma_state_str[ctlr->state]);
+
+ dev_info(dev, "CPDMA: txidver: %x",
+ dma_reg_read(ctlr, CPDMA_TXIDVER));
+ dev_info(dev, "CPDMA: txcontrol: %x",
+ dma_reg_read(ctlr, CPDMA_TXCONTROL));
+ dev_info(dev, "CPDMA: txteardown: %x",
+ dma_reg_read(ctlr, CPDMA_TXTEARDOWN));
+ dev_info(dev, "CPDMA: rxidver: %x",
+ dma_reg_read(ctlr, CPDMA_RXIDVER));
+ dev_info(dev, "CPDMA: rxcontrol: %x",
+ dma_reg_read(ctlr, CPDMA_RXCONTROL));
+ dev_info(dev, "CPDMA: softreset: %x",
+ dma_reg_read(ctlr, CPDMA_SOFTRESET));
+ dev_info(dev, "CPDMA: rxteardown: %x",
+ dma_reg_read(ctlr, CPDMA_RXTEARDOWN));
+ dev_info(dev, "CPDMA: txintstatraw: %x",
+ dma_reg_read(ctlr, CPDMA_TXINTSTATRAW));
+ dev_info(dev, "CPDMA: txintstatmasked: %x",
+ dma_reg_read(ctlr, CPDMA_TXINTSTATMASKED));
+ dev_info(dev, "CPDMA: txintmaskset: %x",
+ dma_reg_read(ctlr, CPDMA_TXINTMASKSET));
+ dev_info(dev, "CPDMA: txintmaskclear: %x",
+ dma_reg_read(ctlr, CPDMA_TXINTMASKCLEAR));
+ dev_info(dev, "CPDMA: macinvector: %x",
+ dma_reg_read(ctlr, CPDMA_MACINVECTOR));
+ dev_info(dev, "CPDMA: maceoivector: %x",
+ dma_reg_read(ctlr, CPDMA_MACEOIVECTOR));
+ dev_info(dev, "CPDMA: rxintstatraw: %x",
+ dma_reg_read(ctlr, CPDMA_RXINTSTATRAW));
+ dev_info(dev, "CPDMA: rxintstatmasked: %x",
+ dma_reg_read(ctlr, CPDMA_RXINTSTATMASKED));
+ dev_info(dev, "CPDMA: rxintmaskset: %x",
+ dma_reg_read(ctlr, CPDMA_RXINTMASKSET));
+ dev_info(dev, "CPDMA: rxintmaskclear: %x",
+ dma_reg_read(ctlr, CPDMA_RXINTMASKCLEAR));
+ dev_info(dev, "CPDMA: dmaintstatraw: %x",
+ dma_reg_read(ctlr, CPDMA_DMAINTSTATRAW));
+ dev_info(dev, "CPDMA: dmaintstatmasked: %x",
+ dma_reg_read(ctlr, CPDMA_DMAINTSTATMASKED));
+ dev_info(dev, "CPDMA: dmaintmaskset: %x",
+ dma_reg_read(ctlr, CPDMA_DMAINTMASKSET));
+ dev_info(dev, "CPDMA: dmaintmaskclear: %x",
+ dma_reg_read(ctlr, CPDMA_DMAINTMASKCLEAR));
+
+ if (!ctlr->params.has_ext_regs) {
+ dev_info(dev, "CPDMA: dmacontrol: %x",
+ dma_reg_read(ctlr, CPDMA_DMACONTROL));
+ dev_info(dev, "CPDMA: dmastatus: %x",
+ dma_reg_read(ctlr, CPDMA_DMASTATUS));
+ dev_info(dev, "CPDMA: rxbuffofs: %x",
+ dma_reg_read(ctlr, CPDMA_RXBUFFOFS));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++)
+ if (ctlr->channels[i])
+ cpdma_chan_dump(ctlr->channels[i]);
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return 0;
+}
+
+int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr)
+{
+ unsigned long flags;
+ int ret = 0, i;
+
+ if (!ctlr)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ if (ctlr->state != CPDMA_STATE_IDLE)
+ cpdma_ctlr_stop(ctlr);
+
+ for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+ if (ctlr->channels[i])
+ cpdma_chan_destroy(ctlr->channels[i]);
+ }
+
+ cpdma_desc_pool_destroy(ctlr->pool);
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ kfree(ctlr);
+ return ret;
+}
+
+int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable)
+{
+ unsigned long flags;
+ int i, reg;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ if (ctlr->state != CPDMA_STATE_ACTIVE) {
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return -EINVAL;
+ }
+
+ reg = enable ? CPDMA_DMAINTMASKSET : CPDMA_DMAINTMASKCLEAR;
+ dma_reg_write(ctlr, reg, CPDMA_DMAINT_HOSTERR);
+
+ for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+ if (ctlr->channels[i])
+ cpdma_chan_int_ctrl(ctlr->channels[i], enable);
+ }
+
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return 0;
+}
+
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr)
+{
+ dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0);
+}
+
+struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
+ cpdma_handler_fn handler)
+{
+ struct cpdma_chan *chan;
+ int ret, offset = (chan_num % CPDMA_MAX_CHANNELS) * 4;
+ unsigned long flags;
+
+ if (__chan_linear(chan_num) >= ctlr->num_chan)
+ return NULL;
+
+ ret = -ENOMEM;
+ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ goto err_chan_alloc;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ ret = -EBUSY;
+ if (ctlr->channels[chan_num])
+ goto err_chan_busy;
+
+ chan->ctlr = ctlr;
+ chan->state = CPDMA_STATE_IDLE;
+ chan->chan_num = chan_num;
+ chan->handler = handler;
+
+ if (is_rx_chan(chan)) {
+ chan->hdp = ctlr->params.rxhdp + offset;
+ chan->cp = ctlr->params.rxcp + offset;
+ chan->rxfree = ctlr->params.rxfree + offset;
+ chan->int_set = CPDMA_RXINTMASKSET;
+ chan->int_clear = CPDMA_RXINTMASKCLEAR;
+ chan->td = CPDMA_RXTEARDOWN;
+ chan->dir = DMA_FROM_DEVICE;
+ } else {
+ chan->hdp = ctlr->params.txhdp + offset;
+ chan->cp = ctlr->params.txcp + offset;
+ chan->int_set = CPDMA_TXINTMASKSET;
+ chan->int_clear = CPDMA_TXINTMASKCLEAR;
+ chan->td = CPDMA_TXTEARDOWN;
+ chan->dir = DMA_TO_DEVICE;
+ }
+ chan->mask = BIT(chan_linear(chan));
+
+ spin_lock_init(&chan->lock);
+
+ ctlr->channels[chan_num] = chan;
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return chan;
+
+err_chan_busy:
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ kfree(chan);
+err_chan_alloc:
+ return ERR_PTR(ret);
+}
+
+int cpdma_chan_destroy(struct cpdma_chan *chan)
+{
+ struct cpdma_ctlr *ctlr = chan->ctlr;
+ unsigned long flags;
+
+ if (!chan)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ if (chan->state != CPDMA_STATE_IDLE)
+ cpdma_chan_stop(chan);
+ ctlr->channels[chan->chan_num] = NULL;
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ kfree(chan);
+ return 0;
+}
+
+int cpdma_chan_get_stats(struct cpdma_chan *chan,
+ struct cpdma_chan_stats *stats)
+{
+ unsigned long flags;
+ if (!chan)
+ return -EINVAL;
+ spin_lock_irqsave(&chan->lock, flags);
+ memcpy(stats, &chan->stats, sizeof(*stats));
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return 0;
+}
+
+int cpdma_chan_dump(struct cpdma_chan *chan)
+{
+ unsigned long flags;
+ struct device *dev = chan->ctlr->dev;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ dev_info(dev, "channel %d (%s %d) state %s",
+ chan->chan_num, is_rx_chan(chan) ? "rx" : "tx",
+ chan_linear(chan), cpdma_state_str[chan->state]);
+ dev_info(dev, "\thdp: %x\n", chan_read(chan, hdp));
+ dev_info(dev, "\tcp: %x\n", chan_read(chan, cp));
+ if (chan->rxfree) {
+ dev_info(dev, "\trxfree: %x\n",
+ chan_read(chan, rxfree));
+ }
+
+ dev_info(dev, "\tstats head_enqueue: %d\n",
+ chan->stats.head_enqueue);
+ dev_info(dev, "\tstats tail_enqueue: %d\n",
+ chan->stats.tail_enqueue);
+ dev_info(dev, "\tstats pad_enqueue: %d\n",
+ chan->stats.pad_enqueue);
+ dev_info(dev, "\tstats misqueued: %d\n",
+ chan->stats.misqueued);
+ dev_info(dev, "\tstats desc_alloc_fail: %d\n",
+ chan->stats.desc_alloc_fail);
+ dev_info(dev, "\tstats pad_alloc_fail: %d\n",
+ chan->stats.pad_alloc_fail);
+ dev_info(dev, "\tstats runt_receive_buff: %d\n",
+ chan->stats.runt_receive_buff);
+ dev_info(dev, "\tstats runt_transmit_buff: %d\n",
+ chan->stats.runt_transmit_buff);
+ dev_info(dev, "\tstats empty_dequeue: %d\n",
+ chan->stats.empty_dequeue);
+ dev_info(dev, "\tstats busy_dequeue: %d\n",
+ chan->stats.busy_dequeue);
+ dev_info(dev, "\tstats good_dequeue: %d\n",
+ chan->stats.good_dequeue);
+ dev_info(dev, "\tstats requeue: %d\n",
+ chan->stats.requeue);
+ dev_info(dev, "\tstats teardown_dequeue: %d\n",
+ chan->stats.teardown_dequeue);
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return 0;
+}
+
+static void __cpdma_chan_submit(struct cpdma_chan *chan,
+ struct cpdma_desc __iomem *desc)
+{
+ struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_desc __iomem *prev = chan->tail;
+ struct cpdma_desc_pool *pool = ctlr->pool;
+ dma_addr_t desc_dma;
+ u32 mode;
+
+ desc_dma = desc_phys(pool, desc);
+
+ /* simple case - idle channel */
+ if (!chan->head) {
+ chan->stats.head_enqueue++;
+ chan->head = desc;
+ chan->tail = desc;
+ if (chan->state == CPDMA_STATE_ACTIVE)
+ chan_write(chan, hdp, desc_dma);
+ return;
+ }
+
+ /* first chain the descriptor at the tail of the list */
+ desc_write(prev, hw_next, desc_dma);
+ chan->tail = desc;
+ chan->stats.tail_enqueue++;
+
+ /* next check if EOQ has been triggered already */
+ mode = desc_read(prev, hw_mode);
+ if (((mode & (CPDMA_DESC_EOQ | CPDMA_DESC_OWNER)) == CPDMA_DESC_EOQ) &&
+ (chan->state == CPDMA_STATE_ACTIVE)) {
+ desc_write(prev, hw_mode, mode & ~CPDMA_DESC_EOQ);
+ chan_write(chan, hdp, desc_dma);
+ chan->stats.misqueued++;
+ }
+}
+
+int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
+ int len, gfp_t gfp_mask)
+{
+ struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_desc __iomem *desc;
+ dma_addr_t buffer;
+ unsigned long flags;
+ u32 mode;
+ int ret = 0;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ if (chan->state == CPDMA_STATE_TEARDOWN) {
+ ret = -EINVAL;
+ goto unlock_ret;
+ }
+
+ desc = cpdma_desc_alloc(ctlr->pool, 1);
+ if (!desc) {
+ chan->stats.desc_alloc_fail++;
+ ret = -ENOMEM;
+ goto unlock_ret;
+ }
+
+ if (len < ctlr->params.min_packet_size) {
+ len = ctlr->params.min_packet_size;
+ chan->stats.runt_transmit_buff++;
+ }
+
+ buffer = dma_map_single(ctlr->dev, data, len, chan->dir);
+ mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP;
+
+ desc_write(desc, hw_next, 0);
+ desc_write(desc, hw_buffer, buffer);
+ desc_write(desc, hw_len, len);
+ desc_write(desc, hw_mode, mode | len);
+ desc_write(desc, sw_token, token);
+ desc_write(desc, sw_buffer, buffer);
+ desc_write(desc, sw_len, len);
+
+ __cpdma_chan_submit(chan, desc);
+
+ if (chan->state == CPDMA_STATE_ACTIVE && chan->rxfree)
+ chan_write(chan, rxfree, 1);
+
+ chan->count++;
+
+unlock_ret:
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return ret;
+}
+
+static void __cpdma_chan_free(struct cpdma_chan *chan,
+ struct cpdma_desc __iomem *desc,
+ int outlen, int status)
+{
+ struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_desc_pool *pool = ctlr->pool;
+ dma_addr_t buff_dma;
+ int origlen;
+ void *token;
+
+ token = (void *)desc_read(desc, sw_token);
+ buff_dma = desc_read(desc, sw_buffer);
+ origlen = desc_read(desc, sw_len);
+
+ dma_unmap_single(ctlr->dev, buff_dma, origlen, chan->dir);
+ cpdma_desc_free(pool, desc, 1);
+ (*chan->handler)(token, outlen, status);
+}
+
+static int __cpdma_chan_process(struct cpdma_chan *chan)
+{
+ struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_desc __iomem *desc;
+ int status, outlen;
+ struct cpdma_desc_pool *pool = ctlr->pool;
+ dma_addr_t desc_dma;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ desc = chan->head;
+ if (!desc) {
+ chan->stats.empty_dequeue++;
+ status = -ENOENT;
+ goto unlock_ret;
+ }
+ desc_dma = desc_phys(pool, desc);
+
+ status = __raw_readl(&desc->hw_mode);
+ outlen = status & 0x7ff;
+ if (status & CPDMA_DESC_OWNER) {
+ chan->stats.busy_dequeue++;
+ status = -EBUSY;
+ goto unlock_ret;
+ }
+ status = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE);
+
+ chan->head = desc_from_phys(pool, desc_read(desc, hw_next));
+ chan_write(chan, cp, desc_dma);
+ chan->count--;
+ chan->stats.good_dequeue++;
+
+ if (status & CPDMA_DESC_EOQ) {
+ chan->stats.requeue++;
+ chan_write(chan, hdp, desc_phys(pool, chan->head));
+ }
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ __cpdma_chan_free(chan, desc, outlen, status);
+ return status;
+
+unlock_ret:
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return status;
+}
+
+int cpdma_chan_process(struct cpdma_chan *chan, int quota)
+{
+ int used = 0, ret = 0;
+
+ if (chan->state != CPDMA_STATE_ACTIVE)
+ return -EINVAL;
+
+ while (used < quota) {
+ ret = __cpdma_chan_process(chan);
+ if (ret < 0)
+ break;
+ used++;
+ }
+ return used;
+}
+
+int cpdma_chan_start(struct cpdma_chan *chan)
+{
+ struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_desc_pool *pool = ctlr->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->state != CPDMA_STATE_IDLE) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EBUSY;
+ }
+ if (ctlr->state != CPDMA_STATE_ACTIVE) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EINVAL;
+ }
+ dma_reg_write(ctlr, chan->int_set, chan->mask);
+ chan->state = CPDMA_STATE_ACTIVE;
+ if (chan->head) {
+ chan_write(chan, hdp, desc_phys(pool, chan->head));
+ if (chan->rxfree)
+ chan_write(chan, rxfree, chan->count);
+ }
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return 0;
+}
+
+int cpdma_chan_stop(struct cpdma_chan *chan)
+{
+ struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_desc_pool *pool = ctlr->pool;
+ unsigned long flags;
+ int ret;
+ unsigned long timeout;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->state != CPDMA_STATE_ACTIVE) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EINVAL;
+ }
+
+ chan->state = CPDMA_STATE_TEARDOWN;
+ dma_reg_write(ctlr, chan->int_clear, chan->mask);
+
+ /* trigger teardown */
+ dma_reg_write(ctlr, chan->td, chan->chan_num);
+
+ /* wait for teardown complete */
+ timeout = jiffies + HZ/10; /* 100 msec */
+ while (time_before(jiffies, timeout)) {
+ u32 cp = chan_read(chan, cp);
+ if ((cp & CPDMA_TEARDOWN_VALUE) == CPDMA_TEARDOWN_VALUE)
+ break;
+ cpu_relax();
+ }
+ WARN_ON(!time_before(jiffies, timeout));
+ chan_write(chan, cp, CPDMA_TEARDOWN_VALUE);
+
+ /* handle completed packets */
+ do {
+ ret = __cpdma_chan_process(chan);
+ if (ret < 0)
+ break;
+ } while ((ret & CPDMA_DESC_TD_COMPLETE) == 0);
+
+ /* remaining packets haven't been tx/rx'ed, clean them up */
+ while (chan->head) {
+ struct cpdma_desc __iomem *desc = chan->head;
+ dma_addr_t next_dma;
+
+ next_dma = desc_read(desc, hw_next);
+ chan->head = desc_from_phys(pool, next_dma);
+ chan->stats.teardown_dequeue++;
+
+ /* issue callback without locks held */
+ spin_unlock_irqrestore(&chan->lock, flags);
+ __cpdma_chan_free(chan, desc, 0, -ENOSYS);
+ spin_lock_irqsave(&chan->lock, flags);
+ }
+
+ chan->state = CPDMA_STATE_IDLE;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return 0;
+}
+
+int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->state != CPDMA_STATE_ACTIVE) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EINVAL;
+ }
+
+ dma_reg_write(chan->ctlr, enable ? chan->int_set : chan->int_clear,
+ chan->mask);
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return 0;
+}
+
+struct cpdma_control_info {
+ u32 reg;
+ u32 shift, mask;
+ int access;
+#define ACCESS_RO BIT(0)
+#define ACCESS_WO BIT(1)
+#define ACCESS_RW (ACCESS_RO | ACCESS_WO)
+};
+
+struct cpdma_control_info controls[] = {
+ [CPDMA_CMD_IDLE] = {CPDMA_DMACONTROL, 3, 1, ACCESS_WO},
+ [CPDMA_COPY_ERROR_FRAMES] = {CPDMA_DMACONTROL, 4, 1, ACCESS_RW},
+ [CPDMA_RX_OFF_LEN_UPDATE] = {CPDMA_DMACONTROL, 2, 1, ACCESS_RW},
+ [CPDMA_RX_OWNERSHIP_FLIP] = {CPDMA_DMACONTROL, 1, 1, ACCESS_RW},
+ [CPDMA_TX_PRIO_FIXED] = {CPDMA_DMACONTROL, 0, 1, ACCESS_RW},
+ [CPDMA_STAT_IDLE] = {CPDMA_DMASTATUS, 31, 1, ACCESS_RO},
+ [CPDMA_STAT_TX_ERR_CODE] = {CPDMA_DMASTATUS, 20, 0xf, ACCESS_RW},
+ [CPDMA_STAT_TX_ERR_CHAN] = {CPDMA_DMASTATUS, 16, 0x7, ACCESS_RW},
+ [CPDMA_STAT_RX_ERR_CODE] = {CPDMA_DMASTATUS, 12, 0xf, ACCESS_RW},
+ [CPDMA_STAT_RX_ERR_CHAN] = {CPDMA_DMASTATUS, 8, 0x7, ACCESS_RW},
+ [CPDMA_RX_BUFFER_OFFSET] = {CPDMA_RXBUFFOFS, 0, 0xffff, ACCESS_RW},
+};
+
+int cpdma_control_get(struct cpdma_ctlr *ctlr, int control)
+{
+ unsigned long flags;
+ struct cpdma_control_info *info = &controls[control];
+ int ret;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ ret = -ENOTSUPP;
+ if (!ctlr->params.has_ext_regs)
+ goto unlock_ret;
+
+ ret = -EINVAL;
+ if (ctlr->state != CPDMA_STATE_ACTIVE)
+ goto unlock_ret;
+
+ ret = -ENOENT;
+ if (control < 0 || control >= ARRAY_SIZE(controls))
+ goto unlock_ret;
+
+ ret = -EPERM;
+ if ((info->access & ACCESS_RO) != ACCESS_RO)
+ goto unlock_ret;
+
+ ret = (dma_reg_read(ctlr, info->reg) >> info->shift) & info->mask;
+
+unlock_ret:
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return ret;
+}
+
+int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value)
+{
+ unsigned long flags;
+ struct cpdma_control_info *info = &controls[control];
+ int ret;
+ u32 val;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+
+ ret = -ENOTSUPP;
+ if (!ctlr->params.has_ext_regs)
+ goto unlock_ret;
+
+ ret = -EINVAL;
+ if (ctlr->state != CPDMA_STATE_ACTIVE)
+ goto unlock_ret;
+
+ ret = -ENOENT;
+ if (control < 0 || control >= ARRAY_SIZE(controls))
+ goto unlock_ret;
+
+ ret = -EPERM;
+ if ((info->access & ACCESS_WO) != ACCESS_WO)
+ goto unlock_ret;
+
+ val = dma_reg_read(ctlr, info->reg);
+ val &= ~(info->mask << info->shift);
+ val |= (value & info->mask) << info->shift;
+ dma_reg_write(ctlr, info->reg, val);
+ ret = 0;
+
+unlock_ret:
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return ret;
+}
diff --git a/drivers/net/davinci_cpdma.h b/drivers/net/davinci_cpdma.h
new file mode 100644
index 000000000000..868e50ebde45
--- /dev/null
+++ b/drivers/net/davinci_cpdma.h
@@ -0,0 +1,108 @@
+/*
+ * Texas Instruments CPDMA Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation 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.
+ */
+#ifndef __DAVINCI_CPDMA_H__
+#define __DAVINCI_CPDMA_H__
+
+#define CPDMA_MAX_CHANNELS BITS_PER_LONG
+
+#define tx_chan_num(chan) (chan)
+#define rx_chan_num(chan) ((chan) + CPDMA_MAX_CHANNELS)
+#define is_rx_chan(chan) ((chan)->chan_num >= CPDMA_MAX_CHANNELS)
+#define is_tx_chan(chan) (!is_rx_chan(chan))
+#define __chan_linear(chan_num) ((chan_num) & (CPDMA_MAX_CHANNELS - 1))
+#define chan_linear(chan) __chan_linear((chan)->chan_num)
+
+struct cpdma_params {
+ struct device *dev;
+ void __iomem *dmaregs;
+ void __iomem *txhdp, *rxhdp, *txcp, *rxcp;
+ void __iomem *rxthresh, *rxfree;
+ int num_chan;
+ bool has_soft_reset;
+ int min_packet_size;
+ u32 desc_mem_phys;
+ int desc_mem_size;
+ int desc_align;
+
+ /*
+ * Some instances of embedded cpdma controllers have extra control and
+ * status registers. The following flag enables access to these
+ * "extended" registers.
+ */
+ bool has_ext_regs;
+};
+
+struct cpdma_chan_stats {
+ u32 head_enqueue;
+ u32 tail_enqueue;
+ u32 pad_enqueue;
+ u32 misqueued;
+ u32 desc_alloc_fail;
+ u32 pad_alloc_fail;
+ u32 runt_receive_buff;
+ u32 runt_transmit_buff;
+ u32 empty_dequeue;
+ u32 busy_dequeue;
+ u32 good_dequeue;
+ u32 requeue;
+ u32 teardown_dequeue;
+};
+
+struct cpdma_ctlr;
+struct cpdma_chan;
+
+typedef void (*cpdma_handler_fn)(void *token, int len, int status);
+
+struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params);
+int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr);
+int cpdma_ctlr_start(struct cpdma_ctlr *ctlr);
+int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr);
+int cpdma_ctlr_dump(struct cpdma_ctlr *ctlr);
+
+struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
+ cpdma_handler_fn handler);
+int cpdma_chan_destroy(struct cpdma_chan *chan);
+int cpdma_chan_start(struct cpdma_chan *chan);
+int cpdma_chan_stop(struct cpdma_chan *chan);
+int cpdma_chan_dump(struct cpdma_chan *chan);
+
+int cpdma_chan_get_stats(struct cpdma_chan *chan,
+ struct cpdma_chan_stats *stats);
+int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
+ int len, gfp_t gfp_mask);
+int cpdma_chan_process(struct cpdma_chan *chan, int quota);
+
+int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable);
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr);
+int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
+
+enum cpdma_control {
+ CPDMA_CMD_IDLE, /* write-only */
+ CPDMA_COPY_ERROR_FRAMES, /* read-write */
+ CPDMA_RX_OFF_LEN_UPDATE, /* read-write */
+ CPDMA_RX_OWNERSHIP_FLIP, /* read-write */
+ CPDMA_TX_PRIO_FIXED, /* read-write */
+ CPDMA_STAT_IDLE, /* read-only */
+ CPDMA_STAT_TX_ERR_CHAN, /* read-only */
+ CPDMA_STAT_TX_ERR_CODE, /* read-only */
+ CPDMA_STAT_RX_ERR_CHAN, /* read-only */
+ CPDMA_STAT_RX_ERR_CODE, /* read-only */
+ CPDMA_RX_BUFFER_OFFSET, /* read-write */
+};
+
+int cpdma_control_get(struct cpdma_ctlr *ctlr, int control);
+int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value);
+
+#endif
diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index 7fbd052ddb0a..2a628d17d178 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -63,6 +63,8 @@
#include <asm/irq.h>
#include <asm/page.h>
+#include "davinci_cpdma.h"
+
static int debug_level;
module_param(debug_level, int, 0);
MODULE_PARM_DESC(debug_level, "DaVinci EMAC debug level (NETIF_MSG bits)");
@@ -113,7 +115,7 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
#define EMAC_DEF_MAX_FRAME_SIZE (1500 + 14 + 4 + 4)
#define EMAC_DEF_TX_CH (0) /* Default 0th channel */
#define EMAC_DEF_RX_CH (0) /* Default 0th channel */
-#define EMAC_DEF_MDIO_TICK_MS (10) /* typically 1 tick=1 ms) */
+#define EMAC_DEF_RX_NUM_DESC (128)
#define EMAC_DEF_MAX_TX_CH (1) /* Max TX channels configured */
#define EMAC_DEF_MAX_RX_CH (1) /* Max RX channels configured */
#define EMAC_POLL_WEIGHT (64) /* Default NAPI poll weight */
@@ -125,7 +127,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
/* EMAC register related defines */
#define EMAC_ALL_MULTI_REG_VALUE (0xFFFFFFFF)
#define EMAC_NUM_MULTICAST_BITS (64)
-#define EMAC_TEARDOWN_VALUE (0xFFFFFFFC)
#define EMAC_TX_CONTROL_TX_ENABLE_VAL (0x1)
#define EMAC_RX_CONTROL_RX_ENABLE_VAL (0x1)
#define EMAC_MAC_HOST_ERR_INTMASK_VAL (0x2)
@@ -212,24 +213,10 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
#define EMAC_DEF_MAX_MULTICAST_ADDRESSES (64) /* Max mcast addr's */
/* EMAC Peripheral Device Register Memory Layout structure */
-#define EMAC_TXIDVER 0x0
-#define EMAC_TXCONTROL 0x4
-#define EMAC_TXTEARDOWN 0x8
-#define EMAC_RXIDVER 0x10
-#define EMAC_RXCONTROL 0x14
-#define EMAC_RXTEARDOWN 0x18
-#define EMAC_TXINTSTATRAW 0x80
-#define EMAC_TXINTSTATMASKED 0x84
-#define EMAC_TXINTMASKSET 0x88
-#define EMAC_TXINTMASKCLEAR 0x8C
#define EMAC_MACINVECTOR 0x90
#define EMAC_DM646X_MACEOIVECTOR 0x94
-#define EMAC_RXINTSTATRAW 0xA0
-#define EMAC_RXINTSTATMASKED 0xA4
-#define EMAC_RXINTMASKSET 0xA8
-#define EMAC_RXINTMASKCLEAR 0xAC
#define EMAC_MACINTSTATRAW 0xB0
#define EMAC_MACINTSTATMASKED 0xB4
#define EMAC_MACINTMASKSET 0xB8
@@ -256,12 +243,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
#define EMAC_MACADDRHI 0x504
#define EMAC_MACINDEX 0x508
-/* EMAC HDP and Completion registors */
-#define EMAC_TXHDP(ch) (0x600 + (ch * 4))
-#define EMAC_RXHDP(ch) (0x620 + (ch * 4))
-#define EMAC_TXCP(ch) (0x640 + (ch * 4))
-#define EMAC_RXCP(ch) (0x660 + (ch * 4))
-
/* EMAC statistics registers */
#define EMAC_RXGOODFRAMES 0x200
#define EMAC_RXBCASTFRAMES 0x204
@@ -303,25 +284,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
#define EMAC_DM644X_INTMIN_INTVL 0x1
#define EMAC_DM644X_INTMAX_INTVL (EMAC_DM644X_EWINTCNT_MASK)
-/* EMAC MDIO related */
-/* Mask & Control defines */
-#define MDIO_CONTROL_CLKDIV (0xFF)
-#define MDIO_CONTROL_ENABLE BIT(30)
-#define MDIO_USERACCESS_GO BIT(31)
-#define MDIO_USERACCESS_WRITE BIT(30)
-#define MDIO_USERACCESS_READ (0)
-#define MDIO_USERACCESS_REGADR (0x1F << 21)
-#define MDIO_USERACCESS_PHYADR (0x1F << 16)
-#define MDIO_USERACCESS_DATA (0xFFFF)
-#define MDIO_USERPHYSEL_LINKSEL BIT(7)
-#define MDIO_VER_MODID (0xFFFF << 16)
-#define MDIO_VER_REVMAJ (0xFF << 8)
-#define MDIO_VER_REVMIN (0xFF)
-
-#define MDIO_USERACCESS(inst) (0x80 + (inst * 8))
-#define MDIO_USERPHYSEL(inst) (0x84 + (inst * 8))
-#define MDIO_CONTROL (0x04)
-
/* EMAC DM646X control module registers */
#define EMAC_DM646X_CMINTCTRL 0x0C
#define EMAC_DM646X_CMRXINTEN 0x14
@@ -345,120 +307,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
/* EMAC Stats Clear Mask */
#define EMAC_STATS_CLR_MASK (0xFFFFFFFF)
-/** net_buf_obj: EMAC network bufferdata structure
- *
- * EMAC network buffer data structure
- */
-struct emac_netbufobj {
- void *buf_token;
- char *data_ptr;
- int length;
-};
-
-/** net_pkt_obj: EMAC network packet data structure
- *
- * EMAC network packet data structure - supports buffer list (for future)
- */
-struct emac_netpktobj {
- void *pkt_token; /* data token may hold tx/rx chan id */
- struct emac_netbufobj *buf_list; /* array of network buffer objects */
- int num_bufs;
- int pkt_length;
-};
-
-/** emac_tx_bd: EMAC TX Buffer descriptor data structure
- *
- * EMAC TX Buffer descriptor data structure
- */
-struct emac_tx_bd {
- int h_next;
- int buff_ptr;
- int off_b_len;
- int mode; /* SOP, EOP, ownership, EOQ, teardown,Qstarv, length */
- struct emac_tx_bd __iomem *next;
- void *buf_token;
-};
-
-/** emac_txch: EMAC TX Channel data structure
- *
- * EMAC TX Channel data structure
- */
-struct emac_txch {
- /* Config related */
- u32 num_bd;
- u32 service_max;
-
- /* CPPI specific */
- u32 alloc_size;
- void __iomem *bd_mem;
- struct emac_tx_bd __iomem *bd_pool_head;
- struct emac_tx_bd __iomem *active_queue_head;
- struct emac_tx_bd __iomem *active_queue_tail;
- struct emac_tx_bd __iomem *last_hw_bdprocessed;
- u32 queue_active;
- u32 teardown_pending;
- u32 *tx_complete;
-
- /** statistics */
- u32 proc_count; /* TX: # of times emac_tx_bdproc is called */
- u32 mis_queued_packets;
- u32 queue_reinit;
- u32 end_of_queue_add;
- u32 out_of_tx_bd;
- u32 no_active_pkts; /* IRQ when there were no packets to process */
- u32 active_queue_count;
-};
-
-/** emac_rx_bd: EMAC RX Buffer descriptor data structure
- *
- * EMAC RX Buffer descriptor data structure
- */
-struct emac_rx_bd {
- int h_next;
- int buff_ptr;
- int off_b_len;
- int mode;
- struct emac_rx_bd __iomem *next;
- void *data_ptr;
- void *buf_token;
-};
-
-/** emac_rxch: EMAC RX Channel data structure
- *
- * EMAC RX Channel data structure
- */
-struct emac_rxch {
- /* configuration info */
- u32 num_bd;
- u32 service_max;
- u32 buf_size;
- char mac_addr[6];
-
- /** CPPI specific */
- u32 alloc_size;
- void __iomem *bd_mem;
- struct emac_rx_bd __iomem *bd_pool_head;
- struct emac_rx_bd __iomem *active_queue_head;
- struct emac_rx_bd __iomem *active_queue_tail;
- u32 queue_active;
- u32 teardown_pending;
-
- /* packet and buffer objects */
- struct emac_netpktobj pkt_queue;
- struct emac_netbufobj buf_queue;
-
- /** statistics */
- u32 proc_count; /* number of times emac_rx_bdproc is called */
- u32 processed_bd;
- u32 recycled_bd;
- u32 out_of_rx_bd;
- u32 out_of_rx_buffers;
- u32 queue_reinit;
- u32 end_of_queue_add;
- u32 end_of_queue;
- u32 mis_queued_packets;
-};
-
/* emac_priv: EMAC private data structure
*
* EMAC adapter private data structure
@@ -469,17 +317,13 @@ struct emac_priv {
struct platform_device *pdev;
struct napi_struct napi;
char mac_addr[6];
- spinlock_t tx_lock;
- spinlock_t rx_lock;
void __iomem *remap_addr;
u32 emac_base_phys;
void __iomem *emac_base;
void __iomem *ctrl_base;
- void __iomem *emac_ctrl_ram;
- u32 ctrl_ram_size;
- u32 hw_ram_addr;
- struct emac_txch *txch[EMAC_DEF_MAX_TX_CH];
- struct emac_rxch *rxch[EMAC_DEF_MAX_RX_CH];
+ struct cpdma_ctlr *dma;
+ struct cpdma_chan *txchan;
+ struct cpdma_chan *rxchan;
u32 link; /* 1=link on, 0=link off */
u32 speed; /* 0=Auto Neg, 1=No PHY, 10,100, 1000 - mbps */
u32 duplex; /* Link duplex: 0=Half, 1=Full */
@@ -493,13 +337,7 @@ struct emac_priv {
u32 mac_hash2;
u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS];
u32 rx_addr_type;
- /* periodic timer required for MDIO polling */
- struct timer_list periodic_timer;
- u32 periodic_ticks;
- u32 timer_active;
- u32 phy_mask;
- /* mii_bus,phy members */
- struct mii_bus *mii_bus;
+ const char *phy_id;
struct phy_device *phydev;
spinlock_t lock;
/*platform specific members*/
@@ -510,19 +348,6 @@ struct emac_priv {
/* clock frequency for EMAC */
static struct clk *emac_clk;
static unsigned long emac_bus_frequency;
-static unsigned long mdio_max_freq;
-
-#define emac_virt_to_phys(addr, priv) \
- (((u32 __force)(addr) - (u32 __force)(priv->emac_ctrl_ram)) \
- + priv->hw_ram_addr)
-
-/* Cache macros - Packet buffers would be from skb pool which is cached */
-#define EMAC_VIRT_NOCACHE(addr) (addr)
-
-/* DM644x does not have BD's in cached memory - so no cache functions */
-#define BD_CACHE_INVALIDATE(addr, size)
-#define BD_CACHE_WRITEBACK(addr, size)
-#define BD_CACHE_WRITEBACK_INVALIDATE(addr, size)
/* EMAC TX Host Error description strings */
static char *emac_txhost_errcodes[16] = {
@@ -548,9 +373,6 @@ static char *emac_rxhost_errcodes[16] = {
#define emac_ctrl_read(reg) ioread32((priv->ctrl_base + (reg)))
#define emac_ctrl_write(reg, val) iowrite32(val, (priv->ctrl_base + (reg)))
-#define emac_mdio_read(reg) ioread32(bus->priv + (reg))
-#define emac_mdio_write(reg, val) iowrite32(val, (bus->priv + (reg)))
-
/**
* emac_dump_regs: Dump important EMAC registers to debug terminal
* @priv: The DaVinci EMAC private adapter structure
@@ -569,20 +391,6 @@ static void emac_dump_regs(struct emac_priv *priv)
emac_ctrl_read(EMAC_CTRL_EWCTL),
emac_ctrl_read(EMAC_CTRL_EWINTTCNT));
}
- dev_info(emac_dev, "EMAC: TXID: %08X %s, RXID: %08X %s\n",
- emac_read(EMAC_TXIDVER),
- ((emac_read(EMAC_TXCONTROL)) ? "enabled" : "disabled"),
- emac_read(EMAC_RXIDVER),
- ((emac_read(EMAC_RXCONTROL)) ? "enabled" : "disabled"));
- dev_info(emac_dev, "EMAC: TXIntRaw:%08X, TxIntMasked: %08X, "\
- "TxIntMasSet: %08X\n", emac_read(EMAC_TXINTSTATRAW),
- emac_read(EMAC_TXINTSTATMASKED), emac_read(EMAC_TXINTMASKSET));
- dev_info(emac_dev, "EMAC: RXIntRaw:%08X, RxIntMasked: %08X, "\
- "RxIntMasSet: %08X\n", emac_read(EMAC_RXINTSTATRAW),
- emac_read(EMAC_RXINTSTATMASKED), emac_read(EMAC_RXINTMASKSET));
- dev_info(emac_dev, "EMAC: MacIntRaw:%08X, MacIntMasked: %08X, "\
- "MacInVector=%08X\n", emac_read(EMAC_MACINTSTATRAW),
- emac_read(EMAC_MACINTSTATMASKED), emac_read(EMAC_MACINVECTOR));
dev_info(emac_dev, "EMAC: EmuControl:%08X, FifoControl: %08X\n",
emac_read(EMAC_EMCONTROL), emac_read(EMAC_FIFOCONTROL));
dev_info(emac_dev, "EMAC: MBPEnable:%08X, RXUnicastSet: %08X, "\
@@ -591,8 +399,6 @@ static void emac_dump_regs(struct emac_priv *priv)
dev_info(emac_dev, "EMAC: MacControl:%08X, MacStatus: %08X, "\
"MacConfig=%08X\n", emac_read(EMAC_MACCONTROL),
emac_read(EMAC_MACSTATUS), emac_read(EMAC_MACCONFIG));
- dev_info(emac_dev, "EMAC: TXHDP[0]:%08X, RXHDP[0]: %08X\n",
- emac_read(EMAC_TXHDP(0)), emac_read(EMAC_RXHDP(0)));
dev_info(emac_dev, "EMAC Statistics\n");
dev_info(emac_dev, "EMAC: rx_good_frames:%d\n",
emac_read(EMAC_RXGOODFRAMES));
@@ -654,11 +460,10 @@ static void emac_dump_regs(struct emac_priv *priv)
emac_read(EMAC_RXMOFOVERRUNS));
dev_info(emac_dev, "EMAC: rx_dma_overruns:%d\n",
emac_read(EMAC_RXDMAOVERRUNS));
+
+ cpdma_ctlr_dump(priv->dma);
}
-/*************************************************************************
- * EMAC MDIO/Phy Functionality
- *************************************************************************/
/**
* emac_get_drvinfo: Get EMAC driver information
* @ndev: The DaVinci EMAC network adapter
@@ -686,7 +491,7 @@ static int emac_get_settings(struct net_device *ndev,
struct ethtool_cmd *ecmd)
{
struct emac_priv *priv = netdev_priv(ndev);
- if (priv->phy_mask)
+ if (priv->phydev)
return phy_ethtool_gset(priv->phydev, ecmd);
else
return -EOPNOTSUPP;
@@ -704,7 +509,7 @@ static int emac_get_settings(struct net_device *ndev,
static int emac_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
{
struct emac_priv *priv = netdev_priv(ndev);
- if (priv->phy_mask)
+ if (priv->phydev)
return phy_ethtool_sset(priv->phydev, ecmd);
else
return -EOPNOTSUPP;
@@ -841,7 +646,7 @@ static void emac_update_phystatus(struct emac_priv *priv)
mac_control = emac_read(EMAC_MACCONTROL);
cur_duplex = (mac_control & EMAC_MACCONTROL_FULLDUPLEXEN) ?
DUPLEX_FULL : DUPLEX_HALF;
- if (priv->phy_mask)
+ if (priv->phydev)
new_duplex = priv->phydev->duplex;
else
new_duplex = DUPLEX_FULL;
@@ -1184,371 +989,68 @@ static irqreturn_t emac_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-/** EMAC on-chip buffer descriptor memory
- *
- * WARNING: Please note that the on chip memory is used for both TX and RX
- * buffer descriptor queues and is equally divided between TX and RX desc's
- * If the number of TX or RX descriptors change this memory pointers need
- * to be adjusted. If external memory is allocated then these pointers can
- * pointer to the memory
- *
- */
-#define EMAC_TX_BD_MEM(priv) ((priv)->emac_ctrl_ram)
-#define EMAC_RX_BD_MEM(priv) ((priv)->emac_ctrl_ram + \
- (((priv)->ctrl_ram_size) >> 1))
-
-/**
- * emac_init_txch: TX channel initialization
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device init to setup a TX channel (allocate buffer desc
- * create free pool and keep ready for transmission
- *
- * Returns success(0) or mem alloc failures error code
- */
-static int emac_init_txch(struct emac_priv *priv, u32 ch)
-{
- struct device *emac_dev = &priv->ndev->dev;
- u32 cnt, bd_size;
- void __iomem *mem;
- struct emac_tx_bd __iomem *curr_bd;
- struct emac_txch *txch = NULL;
-
- txch = kzalloc(sizeof(struct emac_txch), GFP_KERNEL);
- if (NULL == txch) {
- dev_err(emac_dev, "DaVinci EMAC: TX Ch mem alloc failed");
- return -ENOMEM;
- }
- priv->txch[ch] = txch;
- txch->service_max = EMAC_DEF_TX_MAX_SERVICE;
- txch->active_queue_head = NULL;
- txch->active_queue_tail = NULL;
- txch->queue_active = 0;
- txch->teardown_pending = 0;
-
- /* allocate memory for TX CPPI channel on a 4 byte boundry */
- txch->tx_complete = kzalloc(txch->service_max * sizeof(u32),
- GFP_KERNEL);
- if (NULL == txch->tx_complete) {
- dev_err(emac_dev, "DaVinci EMAC: Tx service mem alloc failed");
- kfree(txch);
- return -ENOMEM;
- }
-
- /* allocate buffer descriptor pool align every BD on four word
- * boundry for future requirements */
- bd_size = (sizeof(struct emac_tx_bd) + 0xF) & ~0xF;
- txch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size;
- txch->alloc_size = (((bd_size * txch->num_bd) + 0xF) & ~0xF);
-
- /* alloc TX BD memory */
- txch->bd_mem = EMAC_TX_BD_MEM(priv);
- __memzero((void __force *)txch->bd_mem, txch->alloc_size);
-
- /* initialize the BD linked list */
- mem = (void __force __iomem *)
- (((u32 __force) txch->bd_mem + 0xF) & ~0xF);
- txch->bd_pool_head = NULL;
- for (cnt = 0; cnt < txch->num_bd; cnt++) {
- curr_bd = mem + (cnt * bd_size);
- curr_bd->next = txch->bd_pool_head;
- txch->bd_pool_head = curr_bd;
- }
-
- /* reset statistics counters */
- txch->out_of_tx_bd = 0;
- txch->no_active_pkts = 0;
- txch->active_queue_count = 0;
-
- return 0;
-}
-
-/**
- * emac_cleanup_txch: Book-keep function to clean TX channel resources
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number
- *
- * Called to clean up TX channel resources
- *
- */
-static void emac_cleanup_txch(struct emac_priv *priv, u32 ch)
+static struct sk_buff *emac_rx_alloc(struct emac_priv *priv)
{
- struct emac_txch *txch = priv->txch[ch];
-
- if (txch) {
- if (txch->bd_mem)
- txch->bd_mem = NULL;
- kfree(txch->tx_complete);
- kfree(txch);
- priv->txch[ch] = NULL;
- }
+ struct sk_buff *skb = dev_alloc_skb(priv->rx_buf_size);
+ if (WARN_ON(!skb))
+ return NULL;
+ skb->dev = priv->ndev;
+ skb_reserve(skb, NET_IP_ALIGN);
+ return skb;
}
-/**
- * emac_net_tx_complete: TX packet completion function
- * @priv: The DaVinci EMAC private adapter structure
- * @net_data_tokens: packet token - skb pointer
- * @num_tokens: number of skb's to free
- * @ch: TX channel number
- *
- * Frees the skb once packet is transmitted
- *
- */
-static int emac_net_tx_complete(struct emac_priv *priv,
- void **net_data_tokens,
- int num_tokens, u32 ch)
+static void emac_rx_handler(void *token, int len, int status)
{
- struct net_device *ndev = priv->ndev;
- u32 cnt;
-
- if (unlikely(num_tokens && netif_queue_stopped(ndev)))
- netif_start_queue(ndev);
- for (cnt = 0; cnt < num_tokens; cnt++) {
- struct sk_buff *skb = (struct sk_buff *)net_data_tokens[cnt];
- if (skb == NULL)
- continue;
- ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
+ struct sk_buff *skb = token;
+ struct net_device *ndev = skb->dev;
+ struct emac_priv *priv = netdev_priv(ndev);
+ struct device *emac_dev = &ndev->dev;
+ int ret;
+
+ /* free and bail if we are shutting down */
+ if (unlikely(!netif_running(ndev))) {
dev_kfree_skb_any(skb);
+ return;
}
- return 0;
-}
-
-/**
- * emac_txch_teardown: TX channel teardown
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number
- *
- * Called to teardown TX channel
- *
- */
-static void emac_txch_teardown(struct emac_priv *priv, u32 ch)
-{
- struct device *emac_dev = &priv->ndev->dev;
- u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */
- struct emac_txch *txch = priv->txch[ch];
- struct emac_tx_bd __iomem *curr_bd;
-
- while ((emac_read(EMAC_TXCP(ch)) & EMAC_TEARDOWN_VALUE) !=
- EMAC_TEARDOWN_VALUE) {
- /* wait till tx teardown complete */
- cpu_relax(); /* TODO: check if this helps ... */
- --teardown_cnt;
- if (0 == teardown_cnt) {
- dev_err(emac_dev, "EMAC: TX teardown aborted\n");
- break;
- }
- }
- emac_write(EMAC_TXCP(ch), EMAC_TEARDOWN_VALUE);
-
- /* process sent packets and return skb's to upper layer */
- if (1 == txch->queue_active) {
- curr_bd = txch->active_queue_head;
- while (curr_bd != NULL) {
- dma_unmap_single(emac_dev, curr_bd->buff_ptr,
- curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE,
- DMA_TO_DEVICE);
-
- emac_net_tx_complete(priv, (void __force *)
- &curr_bd->buf_token, 1, ch);
- if (curr_bd != txch->active_queue_tail)
- curr_bd = curr_bd->next;
- else
- break;
- }
- txch->bd_pool_head = txch->active_queue_head;
- txch->active_queue_head =
- txch->active_queue_tail = NULL;
- }
-}
-/**
- * emac_stop_txch: Stop TX channel operation
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number
- *
- * Called to stop TX channel operation
- *
- */
-static void emac_stop_txch(struct emac_priv *priv, u32 ch)
-{
- struct emac_txch *txch = priv->txch[ch];
-
- if (txch) {
- txch->teardown_pending = 1;
- emac_write(EMAC_TXTEARDOWN, 0);
- emac_txch_teardown(priv, ch);
- txch->teardown_pending = 0;
- emac_write(EMAC_TXINTMASKCLEAR, BIT(ch));
+ /* recycle on recieve error */
+ if (status < 0) {
+ ndev->stats.rx_errors++;
+ goto recycle;
}
-}
-/**
- * emac_tx_bdproc: TX buffer descriptor (packet) processing
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number to process buffer descriptors for
- * @budget: number of packets allowed to process
- * @pending: indication to caller that packets are pending to process
- *
- * Processes TX buffer descriptors after packets are transmitted - checks
- * ownership bit on the TX * descriptor and requeues it to free pool & frees
- * the SKB buffer. Only "budget" number of packets are processed and
- * indication of pending packets provided to the caller
- *
- * Returns number of packets processed
- */
-static int emac_tx_bdproc(struct emac_priv *priv, u32 ch, u32 budget)
-{
- struct device *emac_dev = &priv->ndev->dev;
- unsigned long flags;
- u32 frame_status;
- u32 pkts_processed = 0;
- u32 tx_complete_cnt = 0;
- struct emac_tx_bd __iomem *curr_bd;
- struct emac_txch *txch = priv->txch[ch];
- u32 *tx_complete_ptr = txch->tx_complete;
-
- if (unlikely(1 == txch->teardown_pending)) {
- if (netif_msg_tx_err(priv) && net_ratelimit()) {
- dev_err(emac_dev, "DaVinci EMAC:emac_tx_bdproc: "\
- "teardown pending\n");
- }
- return 0; /* dont handle any pkt completions */
- }
+ /* feed received packet up the stack */
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_receive_skb(skb);
+ ndev->stats.rx_bytes += len;
+ ndev->stats.rx_packets++;
- ++txch->proc_count;
- spin_lock_irqsave(&priv->tx_lock, flags);
- curr_bd = txch->active_queue_head;
- if (NULL == curr_bd) {
- emac_write(EMAC_TXCP(ch),
- emac_virt_to_phys(txch->last_hw_bdprocessed, priv));
- txch->no_active_pkts++;
- spin_unlock_irqrestore(&priv->tx_lock, flags);
- return 0;
+ /* alloc a new packet for receive */
+ skb = emac_rx_alloc(priv);
+ if (!skb) {
+ if (netif_msg_rx_err(priv) && net_ratelimit())
+ dev_err(emac_dev, "failed rx buffer alloc\n");
+ return;
}
- BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
- frame_status = curr_bd->mode;
- while ((curr_bd) &&
- ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
- (pkts_processed < budget)) {
- emac_write(EMAC_TXCP(ch), emac_virt_to_phys(curr_bd, priv));
- txch->active_queue_head = curr_bd->next;
- if (frame_status & EMAC_CPPI_EOQ_BIT) {
- if (curr_bd->next) { /* misqueued packet */
- emac_write(EMAC_TXHDP(ch), curr_bd->h_next);
- ++txch->mis_queued_packets;
- } else {
- txch->queue_active = 0; /* end of queue */
- }
- }
- dma_unmap_single(emac_dev, curr_bd->buff_ptr,
- curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE,
- DMA_TO_DEVICE);
-
- *tx_complete_ptr = (u32) curr_bd->buf_token;
- ++tx_complete_ptr;
- ++tx_complete_cnt;
- curr_bd->next = txch->bd_pool_head;
- txch->bd_pool_head = curr_bd;
- --txch->active_queue_count;
- pkts_processed++;
- txch->last_hw_bdprocessed = curr_bd;
- curr_bd = txch->active_queue_head;
- if (curr_bd) {
- BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
- frame_status = curr_bd->mode;
- }
- } /* end of pkt processing loop */
-
- emac_net_tx_complete(priv,
- (void *)&txch->tx_complete[0],
- tx_complete_cnt, ch);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
- return pkts_processed;
+recycle:
+ ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
+ skb_tailroom(skb), GFP_KERNEL);
+ if (WARN_ON(ret < 0))
+ dev_kfree_skb_any(skb);
}
-#define EMAC_ERR_TX_OUT_OF_BD -1
-
-/**
- * emac_send: EMAC Transmit function (internal)
- * @priv: The DaVinci EMAC private adapter structure
- * @pkt: packet pointer (contains skb ptr)
- * @ch: TX channel number
- *
- * Called by the transmit function to queue the packet in EMAC hardware queue
- *
- * Returns success(0) or error code (typically out of desc's)
- */
-static int emac_send(struct emac_priv *priv, struct emac_netpktobj *pkt, u32 ch)
+static void emac_tx_handler(void *token, int len, int status)
{
- unsigned long flags;
- struct emac_tx_bd __iomem *curr_bd;
- struct emac_txch *txch;
- struct emac_netbufobj *buf_list;
-
- txch = priv->txch[ch];
- buf_list = pkt->buf_list; /* get handle to the buffer array */
-
- /* check packet size and pad if short */
- if (pkt->pkt_length < EMAC_DEF_MIN_ETHPKTSIZE) {
- buf_list->length += (EMAC_DEF_MIN_ETHPKTSIZE - pkt->pkt_length);
- pkt->pkt_length = EMAC_DEF_MIN_ETHPKTSIZE;
- }
+ struct sk_buff *skb = token;
+ struct net_device *ndev = skb->dev;
- spin_lock_irqsave(&priv->tx_lock, flags);
- curr_bd = txch->bd_pool_head;
- if (curr_bd == NULL) {
- txch->out_of_tx_bd++;
- spin_unlock_irqrestore(&priv->tx_lock, flags);
- return EMAC_ERR_TX_OUT_OF_BD;
- }
-
- txch->bd_pool_head = curr_bd->next;
- curr_bd->buf_token = buf_list->buf_token;
- curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buf_list->data_ptr,
- buf_list->length, DMA_TO_DEVICE);
- curr_bd->off_b_len = buf_list->length;
- curr_bd->h_next = 0;
- curr_bd->next = NULL;
- curr_bd->mode = (EMAC_CPPI_SOP_BIT | EMAC_CPPI_OWNERSHIP_BIT |
- EMAC_CPPI_EOP_BIT | pkt->pkt_length);
-
- /* flush the packet from cache if write back cache is present */
- BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
-
- /* send the packet */
- if (txch->active_queue_head == NULL) {
- txch->active_queue_head = curr_bd;
- txch->active_queue_tail = curr_bd;
- if (1 != txch->queue_active) {
- emac_write(EMAC_TXHDP(ch),
- emac_virt_to_phys(curr_bd, priv));
- txch->queue_active = 1;
- }
- ++txch->queue_reinit;
- } else {
- register struct emac_tx_bd __iomem *tail_bd;
- register u32 frame_status;
-
- tail_bd = txch->active_queue_tail;
- tail_bd->next = curr_bd;
- txch->active_queue_tail = curr_bd;
- tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
- tail_bd->h_next = (int)emac_virt_to_phys(curr_bd, priv);
- frame_status = tail_bd->mode;
- if (frame_status & EMAC_CPPI_EOQ_BIT) {
- emac_write(EMAC_TXHDP(ch),
- emac_virt_to_phys(curr_bd, priv));
- frame_status &= ~(EMAC_CPPI_EOQ_BIT);
- tail_bd->mode = frame_status;
- ++txch->end_of_queue_add;
- }
- }
- txch->active_queue_count++;
- spin_unlock_irqrestore(&priv->tx_lock, flags);
- return 0;
+ if (unlikely(netif_queue_stopped(ndev)))
+ netif_start_queue(ndev);
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += len;
+ dev_kfree_skb_any(skb);
}
/**
@@ -1565,42 +1067,36 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct device *emac_dev = &ndev->dev;
int ret_code;
- struct emac_netbufobj tx_buf; /* buffer obj-only single frame support */
- struct emac_netpktobj tx_packet; /* packet object */
struct emac_priv *priv = netdev_priv(ndev);
/* If no link, return */
if (unlikely(!priv->link)) {
if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(emac_dev, "DaVinci EMAC: No link to transmit");
- return NETDEV_TX_BUSY;
+ goto fail_tx;
+ }
+
+ ret_code = skb_padto(skb, EMAC_DEF_MIN_ETHPKTSIZE);
+ if (unlikely(ret_code < 0)) {
+ if (netif_msg_tx_err(priv) && net_ratelimit())
+ dev_err(emac_dev, "DaVinci EMAC: packet pad failed");
+ goto fail_tx;
}
- /* Build the buffer and packet objects - Since only single fragment is
- * supported, need not set length and token in both packet & object.
- * Doing so for completeness sake & to show that this needs to be done
- * in multifragment case
- */
- tx_packet.buf_list = &tx_buf;
- tx_packet.num_bufs = 1; /* only single fragment supported */
- tx_packet.pkt_length = skb->len;
- tx_packet.pkt_token = (void *)skb;
- tx_buf.length = skb->len;
- tx_buf.buf_token = (void *)skb;
- tx_buf.data_ptr = skb->data;
- ret_code = emac_send(priv, &tx_packet, EMAC_DEF_TX_CH);
+ ret_code = cpdma_chan_submit(priv->txchan, skb, skb->data, skb->len,
+ GFP_KERNEL);
if (unlikely(ret_code != 0)) {
- if (ret_code == EMAC_ERR_TX_OUT_OF_BD) {
- if (netif_msg_tx_err(priv) && net_ratelimit())
- dev_err(emac_dev, "DaVinci EMAC: xmit() fatal"\
- " err. Out of TX BD's");
- netif_stop_queue(priv->ndev);
- }
- ndev->stats.tx_dropped++;
- return NETDEV_TX_BUSY;
+ if (netif_msg_tx_err(priv) && net_ratelimit())
+ dev_err(emac_dev, "DaVinci EMAC: desc submit failed");
+ goto fail_tx;
}
return NETDEV_TX_OK;
+
+fail_tx:
+ ndev->stats.tx_dropped++;
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
}
/**
@@ -1621,218 +1117,16 @@ static void emac_dev_tx_timeout(struct net_device *ndev)
if (netif_msg_tx_err(priv))
dev_err(emac_dev, "DaVinci EMAC: xmit timeout, restarting TX");
+ emac_dump_regs(priv);
+
ndev->stats.tx_errors++;
emac_int_disable(priv);
- emac_stop_txch(priv, EMAC_DEF_TX_CH);
- emac_cleanup_txch(priv, EMAC_DEF_TX_CH);
- emac_init_txch(priv, EMAC_DEF_TX_CH);
- emac_write(EMAC_TXHDP(0), 0);
- emac_write(EMAC_TXINTMASKSET, BIT(EMAC_DEF_TX_CH));
+ cpdma_chan_stop(priv->txchan);
+ cpdma_chan_start(priv->txchan);
emac_int_enable(priv);
}
/**
- * emac_net_alloc_rx_buf: Allocate a skb for RX
- * @priv: The DaVinci EMAC private adapter structure
- * @buf_size: size of SKB data buffer to allocate
- * @data_token: data token returned (skb handle for storing in buffer desc)
- * @ch: RX channel number
- *
- * Called during RX channel setup - allocates skb buffer of required size
- * and provides the skb handle and allocated buffer data pointer to caller
- *
- * Returns skb data pointer or 0 on failure to alloc skb
- */
-static void *emac_net_alloc_rx_buf(struct emac_priv *priv, int buf_size,
- void **data_token, u32 ch)
-{
- struct net_device *ndev = priv->ndev;
- struct device *emac_dev = &ndev->dev;
- struct sk_buff *p_skb;
-
- p_skb = dev_alloc_skb(buf_size);
- if (unlikely(NULL == p_skb)) {
- if (netif_msg_rx_err(priv) && net_ratelimit())
- dev_err(emac_dev, "DaVinci EMAC: failed to alloc skb");
- return NULL;
- }
-
- /* set device pointer in skb and reserve space for extra bytes */
- p_skb->dev = ndev;
- skb_reserve(p_skb, NET_IP_ALIGN);
- *data_token = (void *) p_skb;
- return p_skb->data;
-}
-
-/**
- * emac_init_rxch: RX channel initialization
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- * @param: mac address for RX channel
- *
- * Called during device init to setup a RX channel (allocate buffers and
- * buffer descriptors, create queue and keep ready for reception
- *
- * Returns success(0) or mem alloc failures error code
- */
-static int emac_init_rxch(struct emac_priv *priv, u32 ch, char *param)
-{
- struct device *emac_dev = &priv->ndev->dev;
- u32 cnt, bd_size;
- void __iomem *mem;
- struct emac_rx_bd __iomem *curr_bd;
- struct emac_rxch *rxch = NULL;
-
- rxch = kzalloc(sizeof(struct emac_rxch), GFP_KERNEL);
- if (NULL == rxch) {
- dev_err(emac_dev, "DaVinci EMAC: RX Ch mem alloc failed");
- return -ENOMEM;
- }
- priv->rxch[ch] = rxch;
- rxch->buf_size = priv->rx_buf_size;
- rxch->service_max = EMAC_DEF_RX_MAX_SERVICE;
- rxch->queue_active = 0;
- rxch->teardown_pending = 0;
-
- /* save mac address */
- for (cnt = 0; cnt < 6; cnt++)
- rxch->mac_addr[cnt] = param[cnt];
-
- /* allocate buffer descriptor pool align every BD on four word
- * boundry for future requirements */
- bd_size = (sizeof(struct emac_rx_bd) + 0xF) & ~0xF;
- rxch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size;
- rxch->alloc_size = (((bd_size * rxch->num_bd) + 0xF) & ~0xF);
- rxch->bd_mem = EMAC_RX_BD_MEM(priv);
- __memzero((void __force *)rxch->bd_mem, rxch->alloc_size);
- rxch->pkt_queue.buf_list = &rxch->buf_queue;
-
- /* allocate RX buffer and initialize the BD linked list */
- mem = (void __force __iomem *)
- (((u32 __force) rxch->bd_mem + 0xF) & ~0xF);
- rxch->active_queue_head = NULL;
- rxch->active_queue_tail = mem;
- for (cnt = 0; cnt < rxch->num_bd; cnt++) {
- curr_bd = mem + (cnt * bd_size);
- /* for future use the last parameter contains the BD ptr */
- curr_bd->data_ptr = emac_net_alloc_rx_buf(priv,
- rxch->buf_size,
- (void __force **)&curr_bd->buf_token,
- EMAC_DEF_RX_CH);
- if (curr_bd->data_ptr == NULL) {
- dev_err(emac_dev, "DaVinci EMAC: RX buf mem alloc " \
- "failed for ch %d\n", ch);
- kfree(rxch);
- return -ENOMEM;
- }
-
- /* populate the hardware descriptor */
- curr_bd->h_next = emac_virt_to_phys(rxch->active_queue_head,
- priv);
- curr_bd->buff_ptr = dma_map_single(emac_dev, curr_bd->data_ptr,
- rxch->buf_size, DMA_FROM_DEVICE);
- curr_bd->off_b_len = rxch->buf_size;
- curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
-
- /* write back to hardware memory */
- BD_CACHE_WRITEBACK_INVALIDATE((u32) curr_bd,
- EMAC_BD_LENGTH_FOR_CACHE);
- curr_bd->next = rxch->active_queue_head;
- rxch->active_queue_head = curr_bd;
- }
-
- /* At this point rxCppi->activeQueueHead points to the first
- RX BD ready to be given to RX HDP and rxch->active_queue_tail
- points to the last RX BD
- */
- return 0;
-}
-
-/**
- * emac_rxch_teardown: RX channel teardown
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device stop to teardown RX channel
- *
- */
-static void emac_rxch_teardown(struct emac_priv *priv, u32 ch)
-{
- struct device *emac_dev = &priv->ndev->dev;
- u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */
-
- while ((emac_read(EMAC_RXCP(ch)) & EMAC_TEARDOWN_VALUE) !=
- EMAC_TEARDOWN_VALUE) {
- /* wait till tx teardown complete */
- cpu_relax(); /* TODO: check if this helps ... */
- --teardown_cnt;
- if (0 == teardown_cnt) {
- dev_err(emac_dev, "EMAC: RX teardown aborted\n");
- break;
- }
- }
- emac_write(EMAC_RXCP(ch), EMAC_TEARDOWN_VALUE);
-}
-
-/**
- * emac_stop_rxch: Stop RX channel operation
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device stop to stop RX channel operation
- *
- */
-static void emac_stop_rxch(struct emac_priv *priv, u32 ch)
-{
- struct emac_rxch *rxch = priv->rxch[ch];
-
- if (rxch) {
- rxch->teardown_pending = 1;
- emac_write(EMAC_RXTEARDOWN, ch);
- /* wait for teardown complete */
- emac_rxch_teardown(priv, ch);
- rxch->teardown_pending = 0;
- emac_write(EMAC_RXINTMASKCLEAR, BIT(ch));
- }
-}
-
-/**
- * emac_cleanup_rxch: Book-keep function to clean RX channel resources
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device stop to clean up RX channel resources
- *
- */
-static void emac_cleanup_rxch(struct emac_priv *priv, u32 ch)
-{
- struct emac_rxch *rxch = priv->rxch[ch];
- struct emac_rx_bd __iomem *curr_bd;
-
- if (rxch) {
- /* free the receive buffers previously allocated */
- curr_bd = rxch->active_queue_head;
- while (curr_bd) {
- if (curr_bd->buf_token) {
- dma_unmap_single(&priv->ndev->dev,
- curr_bd->buff_ptr,
- curr_bd->off_b_len
- & EMAC_RX_BD_BUF_SIZE,
- DMA_FROM_DEVICE);
-
- dev_kfree_skb_any((struct sk_buff *)\
- curr_bd->buf_token);
- }
- curr_bd = curr_bd->next;
- }
- if (rxch->bd_mem)
- rxch->bd_mem = NULL;
- kfree(rxch);
- priv->rxch[ch] = NULL;
- }
-}
-
-/**
* emac_set_type0addr: Set EMAC Type0 mac address
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
@@ -1948,7 +1242,6 @@ static void emac_setmac(struct emac_priv *priv, u32 ch, char *mac_addr)
static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
{
struct emac_priv *priv = netdev_priv(ndev);
- struct emac_rxch *rxch = priv->rxch[EMAC_DEF_RX_CH];
struct device *emac_dev = &priv->ndev->dev;
struct sockaddr *sa = addr;
@@ -1959,11 +1252,10 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len);
- /* If the interface is down - rxch is NULL. */
/* MAC address is configured only after the interface is enabled. */
if (netif_running(ndev)) {
- memcpy(rxch->mac_addr, sa->sa_data, ndev->addr_len);
- emac_setmac(priv, EMAC_DEF_RX_CH, rxch->mac_addr);
+ memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
+ emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr);
}
if (netif_msg_drv(priv))
@@ -1974,194 +1266,6 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
}
/**
- * emac_addbd_to_rx_queue: Recycle RX buffer descriptor
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number to process buffer descriptors for
- * @curr_bd: current buffer descriptor
- * @buffer: buffer pointer for descriptor
- * @buf_token: buffer token (stores skb information)
- *
- * Prepares the recycled buffer descriptor and addes it to hardware
- * receive queue - if queue empty this descriptor becomes the head
- * else addes the descriptor to end of queue
- *
- */
-static void emac_addbd_to_rx_queue(struct emac_priv *priv, u32 ch,
- struct emac_rx_bd __iomem *curr_bd,
- char *buffer, void *buf_token)
-{
- struct emac_rxch *rxch = priv->rxch[ch];
-
- /* populate the hardware descriptor */
- curr_bd->h_next = 0;
- curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buffer,
- rxch->buf_size, DMA_FROM_DEVICE);
- curr_bd->off_b_len = rxch->buf_size;
- curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
- curr_bd->next = NULL;
- curr_bd->data_ptr = buffer;
- curr_bd->buf_token = buf_token;
-
- /* write back */
- BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
- if (rxch->active_queue_head == NULL) {
- rxch->active_queue_head = curr_bd;
- rxch->active_queue_tail = curr_bd;
- if (0 != rxch->queue_active) {
- emac_write(EMAC_RXHDP(ch),
- emac_virt_to_phys(rxch->active_queue_head, priv));
- rxch->queue_active = 1;
- }
- } else {
- struct emac_rx_bd __iomem *tail_bd;
- u32 frame_status;
-
- tail_bd = rxch->active_queue_tail;
- rxch->active_queue_tail = curr_bd;
- tail_bd->next = curr_bd;
- tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
- tail_bd->h_next = emac_virt_to_phys(curr_bd, priv);
- frame_status = tail_bd->mode;
- if (frame_status & EMAC_CPPI_EOQ_BIT) {
- emac_write(EMAC_RXHDP(ch),
- emac_virt_to_phys(curr_bd, priv));
- frame_status &= ~(EMAC_CPPI_EOQ_BIT);
- tail_bd->mode = frame_status;
- ++rxch->end_of_queue_add;
- }
- }
- ++rxch->recycled_bd;
-}
-
-/**
- * emac_net_rx_cb: Prepares packet and sends to upper layer
- * @priv: The DaVinci EMAC private adapter structure
- * @net_pkt_list: Network packet list (received packets)
- *
- * Invalidates packet buffer memory and sends the received packet to upper
- * layer
- *
- * Returns success or appropriate error code (none as of now)
- */
-static int emac_net_rx_cb(struct emac_priv *priv,
- struct emac_netpktobj *net_pkt_list)
-{
- struct net_device *ndev = priv->ndev;
- struct sk_buff *p_skb = net_pkt_list->pkt_token;
- /* set length of packet */
- skb_put(p_skb, net_pkt_list->pkt_length);
- p_skb->protocol = eth_type_trans(p_skb, priv->ndev);
- netif_receive_skb(p_skb);
- ndev->stats.rx_bytes += net_pkt_list->pkt_length;
- ndev->stats.rx_packets++;
- return 0;
-}
-
-/**
- * emac_rx_bdproc: RX buffer descriptor (packet) processing
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number to process buffer descriptors for
- * @budget: number of packets allowed to process
- * @pending: indication to caller that packets are pending to process
- *
- * Processes RX buffer descriptors - checks ownership bit on the RX buffer
- * descriptor, sends the receive packet to upper layer, allocates a new SKB
- * and recycles the buffer descriptor (requeues it in hardware RX queue).
- * Only "budget" number of packets are processed and indication of pending
- * packets provided to the caller.
- *
- * Returns number of packets processed (and indication of pending packets)
- */
-static int emac_rx_bdproc(struct emac_priv *priv, u32 ch, u32 budget)
-{
- unsigned long flags;
- u32 frame_status;
- u32 pkts_processed = 0;
- char *new_buffer;
- struct emac_rx_bd __iomem *curr_bd;
- struct emac_rx_bd __iomem *last_bd;
- struct emac_netpktobj *curr_pkt, pkt_obj;
- struct emac_netbufobj buf_obj;
- struct emac_netbufobj *rx_buf_obj;
- void *new_buf_token;
- struct emac_rxch *rxch = priv->rxch[ch];
-
- if (unlikely(1 == rxch->teardown_pending))
- return 0;
- ++rxch->proc_count;
- spin_lock_irqsave(&priv->rx_lock, flags);
- pkt_obj.buf_list = &buf_obj;
- curr_pkt = &pkt_obj;
- curr_bd = rxch->active_queue_head;
- BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
- frame_status = curr_bd->mode;
-
- while ((curr_bd) &&
- ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
- (pkts_processed < budget)) {
-
- new_buffer = emac_net_alloc_rx_buf(priv, rxch->buf_size,
- &new_buf_token, EMAC_DEF_RX_CH);
- if (unlikely(NULL == new_buffer)) {
- ++rxch->out_of_rx_buffers;
- goto end_emac_rx_bdproc;
- }
-
- /* populate received packet data structure */
- rx_buf_obj = &curr_pkt->buf_list[0];
- rx_buf_obj->data_ptr = (char *)curr_bd->data_ptr;
- rx_buf_obj->length = curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE;
- rx_buf_obj->buf_token = curr_bd->buf_token;
-
- dma_unmap_single(&priv->ndev->dev, curr_bd->buff_ptr,
- curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE,
- DMA_FROM_DEVICE);
-
- curr_pkt->pkt_token = curr_pkt->buf_list->buf_token;
- curr_pkt->num_bufs = 1;
- curr_pkt->pkt_length =
- (frame_status & EMAC_RX_BD_PKT_LENGTH_MASK);
- emac_write(EMAC_RXCP(ch), emac_virt_to_phys(curr_bd, priv));
- ++rxch->processed_bd;
- last_bd = curr_bd;
- curr_bd = last_bd->next;
- rxch->active_queue_head = curr_bd;
-
- /* check if end of RX queue ? */
- if (frame_status & EMAC_CPPI_EOQ_BIT) {
- if (curr_bd) {
- ++rxch->mis_queued_packets;
- emac_write(EMAC_RXHDP(ch),
- emac_virt_to_phys(curr_bd, priv));
- } else {
- ++rxch->end_of_queue;
- rxch->queue_active = 0;
- }
- }
-
- /* recycle BD */
- emac_addbd_to_rx_queue(priv, ch, last_bd, new_buffer,
- new_buf_token);
-
- /* return the packet to the user - BD ptr passed in
- * last parameter for potential *future* use */
- spin_unlock_irqrestore(&priv->rx_lock, flags);
- emac_net_rx_cb(priv, curr_pkt);
- spin_lock_irqsave(&priv->rx_lock, flags);
- curr_bd = rxch->active_queue_head;
- if (curr_bd) {
- BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
- frame_status = curr_bd->mode;
- }
- ++pkts_processed;
- }
-
-end_emac_rx_bdproc:
- spin_unlock_irqrestore(&priv->rx_lock, flags);
- return pkts_processed;
-}
-
-/**
* emac_hw_enable: Enable EMAC hardware for packet transmission/reception
* @priv: The DaVinci EMAC private adapter structure
*
@@ -2172,7 +1276,7 @@ end_emac_rx_bdproc:
*/
static int emac_hw_enable(struct emac_priv *priv)
{
- u32 ch, val, mbp_enable, mac_control;
+ u32 val, mbp_enable, mac_control;
/* Soft reset */
emac_write(EMAC_SOFTRESET, 1);
@@ -2215,26 +1319,9 @@ static int emac_hw_enable(struct emac_priv *priv)
emac_write(EMAC_RXUNICASTCLEAR, EMAC_RX_UNICAST_CLEAR_ALL);
priv->rx_addr_type = (emac_read(EMAC_MACCONFIG) >> 8) & 0xFF;
- val = emac_read(EMAC_TXCONTROL);
- val |= EMAC_TX_CONTROL_TX_ENABLE_VAL;
- emac_write(EMAC_TXCONTROL, val);
- val = emac_read(EMAC_RXCONTROL);
- val |= EMAC_RX_CONTROL_RX_ENABLE_VAL;
- emac_write(EMAC_RXCONTROL, val);
emac_write(EMAC_MACINTMASKSET, EMAC_MAC_HOST_ERR_INTMASK_VAL);
- for (ch = 0; ch < EMAC_DEF_MAX_TX_CH; ch++) {
- emac_write(EMAC_TXHDP(ch), 0);
- emac_write(EMAC_TXINTMASKSET, BIT(ch));
- }
- for (ch = 0; ch < EMAC_DEF_MAX_RX_CH; ch++) {
- struct emac_rxch *rxch = priv->rxch[ch];
- emac_setmac(priv, ch, rxch->mac_addr);
- emac_write(EMAC_RXINTMASKSET, BIT(ch));
- rxch->queue_active = 1;
- emac_write(EMAC_RXHDP(ch),
- emac_virt_to_phys(rxch->active_queue_head, priv));
- }
+ emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr);
/* Enable MII */
val = emac_read(EMAC_MACCONTROL);
@@ -2279,8 +1366,8 @@ static int emac_poll(struct napi_struct *napi, int budget)
mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC;
if (status & mask) {
- num_tx_pkts = emac_tx_bdproc(priv, EMAC_DEF_TX_CH,
- EMAC_DEF_TX_MAX_SERVICE);
+ num_tx_pkts = cpdma_chan_process(priv->txchan,
+ EMAC_DEF_TX_MAX_SERVICE);
} /* TX processing */
mask = EMAC_DM644X_MAC_IN_VECTOR_RX_INT_VEC;
@@ -2289,7 +1376,7 @@ static int emac_poll(struct napi_struct *napi, int budget)
mask = EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC;
if (status & mask) {
- num_rx_pkts = emac_rx_bdproc(priv, EMAC_DEF_RX_CH, budget);
+ num_rx_pkts = cpdma_chan_process(priv->rxchan, budget);
} /* RX processing */
mask = EMAC_DM644X_MAC_IN_VECTOR_HOST_INT;
@@ -2348,79 +1435,6 @@ void emac_poll_controller(struct net_device *ndev)
}
#endif
-/* PHY/MII bus related */
-
-/* Wait until mdio is ready for next command */
-#define MDIO_WAIT_FOR_USER_ACCESS\
- while ((emac_mdio_read((MDIO_USERACCESS(0))) &\
- MDIO_USERACCESS_GO) != 0)
-
-static int emac_mii_read(struct mii_bus *bus, int phy_id, int phy_reg)
-{
- unsigned int phy_data = 0;
- unsigned int phy_control;
-
- /* Wait until mdio is ready for next command */
- MDIO_WAIT_FOR_USER_ACCESS;
-
- phy_control = (MDIO_USERACCESS_GO |
- MDIO_USERACCESS_READ |
- ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
- ((phy_id << 16) & MDIO_USERACCESS_PHYADR) |
- (phy_data & MDIO_USERACCESS_DATA));
- emac_mdio_write(MDIO_USERACCESS(0), phy_control);
-
- /* Wait until mdio is ready for next command */
- MDIO_WAIT_FOR_USER_ACCESS;
-
- return emac_mdio_read(MDIO_USERACCESS(0)) & MDIO_USERACCESS_DATA;
-
-}
-
-static int emac_mii_write(struct mii_bus *bus, int phy_id,
- int phy_reg, u16 phy_data)
-{
-
- unsigned int control;
-
- /* until mdio is ready for next command */
- MDIO_WAIT_FOR_USER_ACCESS;
-
- control = (MDIO_USERACCESS_GO |
- MDIO_USERACCESS_WRITE |
- ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
- ((phy_id << 16) & MDIO_USERACCESS_PHYADR) |
- (phy_data & MDIO_USERACCESS_DATA));
- emac_mdio_write(MDIO_USERACCESS(0), control);
-
- return 0;
-}
-
-static int emac_mii_reset(struct mii_bus *bus)
-{
- unsigned int clk_div;
- int mdio_bus_freq = emac_bus_frequency;
-
- if (mdio_max_freq && mdio_bus_freq)
- clk_div = ((mdio_bus_freq / mdio_max_freq) - 1);
- else
- clk_div = 0xFF;
-
- clk_div &= MDIO_CONTROL_CLKDIV;
-
- /* Set enable and clock divider in MDIOControl */
- emac_mdio_write(MDIO_CONTROL, (clk_div | MDIO_CONTROL_ENABLE));
-
- return 0;
-
-}
-
-static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, PHY_POLL };
-
-/* emac_driver: EMAC MII bus structure */
-
-static struct mii_bus *emac_mii;
-
static void emac_adjust_link(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
@@ -2485,6 +1499,11 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd)
return -EOPNOTSUPP;
}
+static int match_first_device(struct device *dev, void *data)
+{
+ return 1;
+}
+
/**
* emac_dev_open: EMAC device open
* @ndev: The DaVinci EMAC network adapter
@@ -2498,10 +1517,9 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd)
static int emac_dev_open(struct net_device *ndev)
{
struct device *emac_dev = &ndev->dev;
- u32 rc, cnt, ch;
- int phy_addr;
+ u32 cnt;
struct resource *res;
- int q, m;
+ int q, m, ret;
int i = 0;
int k = 0;
struct emac_priv *priv = netdev_priv(ndev);
@@ -2513,29 +1531,21 @@ static int emac_dev_open(struct net_device *ndev)
/* Configuration items */
priv->rx_buf_size = EMAC_DEF_MAX_FRAME_SIZE + NET_IP_ALIGN;
- /* Clear basic hardware */
- for (ch = 0; ch < EMAC_MAX_TXRX_CHANNELS; ch++) {
- emac_write(EMAC_TXHDP(ch), 0);
- emac_write(EMAC_RXHDP(ch), 0);
- emac_write(EMAC_RXHDP(ch), 0);
- emac_write(EMAC_RXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
- emac_write(EMAC_TXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
- }
priv->mac_hash1 = 0;
priv->mac_hash2 = 0;
emac_write(EMAC_MACHASH1, 0);
emac_write(EMAC_MACHASH2, 0);
- /* multi ch not supported - open 1 TX, 1RX ch by default */
- rc = emac_init_txch(priv, EMAC_DEF_TX_CH);
- if (0 != rc) {
- dev_err(emac_dev, "DaVinci EMAC: emac_init_txch() failed");
- return rc;
- }
- rc = emac_init_rxch(priv, EMAC_DEF_RX_CH, priv->mac_addr);
- if (0 != rc) {
- dev_err(emac_dev, "DaVinci EMAC: emac_init_rxch() failed");
- return rc;
+ for (i = 0; i < EMAC_DEF_RX_NUM_DESC; i++) {
+ struct sk_buff *skb = emac_rx_alloc(priv);
+
+ if (!skb)
+ break;
+
+ ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
+ skb_tailroom(skb), GFP_KERNEL);
+ if (WARN_ON(ret < 0))
+ break;
}
/* Request IRQ */
@@ -2560,28 +1570,28 @@ static int emac_dev_open(struct net_device *ndev)
emac_set_coalesce(ndev, &coal);
}
- /* find the first phy */
+ cpdma_ctlr_start(priv->dma);
+
priv->phydev = NULL;
- if (priv->phy_mask) {
- emac_mii_reset(priv->mii_bus);
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- if (priv->mii_bus->phy_map[phy_addr]) {
- priv->phydev = priv->mii_bus->phy_map[phy_addr];
- break;
- }
- }
+ /* use the first phy on the bus if pdata did not give us a phy id */
+ if (!priv->phy_id) {
+ struct device *phy;
- if (!priv->phydev) {
- printk(KERN_ERR "%s: no PHY found\n", ndev->name);
- return -1;
- }
+ phy = bus_find_device(&mdio_bus_type, NULL, NULL,
+ match_first_device);
+ if (phy)
+ priv->phy_id = dev_name(phy);
+ }
- priv->phydev = phy_connect(ndev, dev_name(&priv->phydev->dev),
- &emac_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+ if (priv->phy_id && *priv->phy_id) {
+ priv->phydev = phy_connect(ndev, priv->phy_id,
+ &emac_adjust_link, 0,
+ PHY_INTERFACE_MODE_MII);
if (IS_ERR(priv->phydev)) {
- printk(KERN_ERR "%s: Could not attach to PHY\n",
- ndev->name);
+ dev_err(emac_dev, "could not connect to phy %s\n",
+ priv->phy_id);
+ priv->phydev = NULL;
return PTR_ERR(priv->phydev);
}
@@ -2589,12 +1599,13 @@ static int emac_dev_open(struct net_device *ndev)
priv->speed = 0;
priv->duplex = ~0;
- printk(KERN_INFO "%s: attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, id=%x)\n", ndev->name,
+ dev_info(emac_dev, "attached PHY driver [%s] "
+ "(mii_bus:phy_addr=%s, id=%x)\n",
priv->phydev->drv->name, dev_name(&priv->phydev->dev),
priv->phydev->phy_id);
- } else{
+ } else {
/* No PHY , fix the link, speed and duplex settings */
+ dev_notice(emac_dev, "no phy, defaulting to 100/full\n");
priv->link = 1;
priv->speed = SPEED_100;
priv->duplex = DUPLEX_FULL;
@@ -2607,7 +1618,7 @@ static int emac_dev_open(struct net_device *ndev)
if (netif_msg_drv(priv))
dev_notice(emac_dev, "DaVinci EMAC: Opened %s\n", ndev->name);
- if (priv->phy_mask)
+ if (priv->phydev)
phy_start(priv->phydev);
return 0;
@@ -2648,10 +1659,7 @@ static int emac_dev_stop(struct net_device *ndev)
netif_carrier_off(ndev);
emac_int_disable(priv);
- emac_stop_txch(priv, EMAC_DEF_TX_CH);
- emac_stop_rxch(priv, EMAC_DEF_RX_CH);
- emac_cleanup_txch(priv, EMAC_DEF_TX_CH);
- emac_cleanup_rxch(priv, EMAC_DEF_RX_CH);
+ cpdma_ctlr_stop(priv->dma);
emac_write(EMAC_SOFTRESET, 1);
if (priv->phydev)
@@ -2756,9 +1764,10 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
struct resource *res;
struct net_device *ndev;
struct emac_priv *priv;
- unsigned long size;
+ unsigned long size, hw_ram_addr;
struct emac_platform_data *pdata;
struct device *emac_dev;
+ struct cpdma_params dma_params;
/* obtain emac clock from kernel */
emac_clk = clk_get(&pdev->dev, NULL);
@@ -2782,8 +1791,6 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
priv->ndev = ndev;
priv->msg_enable = netif_msg_init(debug_level, DAVINCI_EMAC_DEBUG);
- spin_lock_init(&priv->tx_lock);
- spin_lock_init(&priv->rx_lock);
spin_lock_init(&priv->lock);
pdata = pdev->dev.platform_data;
@@ -2794,7 +1801,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
/* MAC addr and PHY mask , RMII enable info from platform_data */
memcpy(priv->mac_addr, pdata->mac_addr, 6);
- priv->phy_mask = pdata->phy_mask;
+ priv->phy_id = pdata->phy_id;
priv->rmii_en = pdata->rmii_en;
priv->version = pdata->version;
priv->int_enable = pdata->interrupt_enable;
@@ -2831,14 +1838,41 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
ndev->base_addr = (unsigned long)priv->remap_addr;
priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset;
- priv->ctrl_ram_size = pdata->ctrl_ram_size;
- priv->emac_ctrl_ram = priv->remap_addr + pdata->ctrl_ram_offset;
- if (pdata->hw_ram_addr)
- priv->hw_ram_addr = pdata->hw_ram_addr;
- else
- priv->hw_ram_addr = (u32 __force)res->start +
- pdata->ctrl_ram_offset;
+ hw_ram_addr = pdata->hw_ram_addr;
+ if (!hw_ram_addr)
+ hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset;
+
+ memset(&dma_params, 0, sizeof(dma_params));
+ dma_params.dev = emac_dev;
+ dma_params.dmaregs = priv->emac_base;
+ dma_params.rxthresh = priv->emac_base + 0x120;
+ dma_params.rxfree = priv->emac_base + 0x140;
+ dma_params.txhdp = priv->emac_base + 0x600;
+ dma_params.rxhdp = priv->emac_base + 0x620;
+ dma_params.txcp = priv->emac_base + 0x640;
+ dma_params.rxcp = priv->emac_base + 0x660;
+ dma_params.num_chan = EMAC_MAX_TXRX_CHANNELS;
+ dma_params.min_packet_size = EMAC_DEF_MIN_ETHPKTSIZE;
+ dma_params.desc_mem_phys = hw_ram_addr;
+ dma_params.desc_mem_size = pdata->ctrl_ram_size;
+ dma_params.desc_align = 16;
+
+ priv->dma = cpdma_ctlr_create(&dma_params);
+ if (!priv->dma) {
+ dev_err(emac_dev, "DaVinci EMAC: Error initializing DMA\n");
+ rc = -ENOMEM;
+ goto no_dma;
+ }
+
+ priv->txchan = cpdma_chan_create(priv->dma, tx_chan_num(EMAC_DEF_TX_CH),
+ emac_tx_handler);
+ priv->rxchan = cpdma_chan_create(priv->dma, rx_chan_num(EMAC_DEF_RX_CH),
+ emac_rx_handler);
+ if (WARN_ON(!priv->txchan || !priv->rxchan)) {
+ rc = -ENOMEM;
+ goto no_irq_res;
+ }
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
@@ -2871,32 +1905,6 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
}
- /* MII/Phy intialisation, mdio bus registration */
- emac_mii = mdiobus_alloc();
- if (emac_mii == NULL) {
- dev_err(emac_dev, "DaVinci EMAC: Error allocating mii_bus\n");
- rc = -ENOMEM;
- goto mdio_alloc_err;
- }
-
- priv->mii_bus = emac_mii;
- emac_mii->name = "emac-mii",
- emac_mii->read = emac_mii_read,
- emac_mii->write = emac_mii_write,
- emac_mii->reset = emac_mii_reset,
- emac_mii->irq = mii_irqs,
- emac_mii->phy_mask = ~(priv->phy_mask);
- emac_mii->parent = &pdev->dev;
- emac_mii->priv = priv->remap_addr + pdata->mdio_reg_offset;
- snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%x", priv->pdev->id);
- mdio_max_freq = pdata->mdio_max_freq;
- emac_mii->reset(emac_mii);
-
- /* Register the MII bus */
- rc = mdiobus_register(emac_mii);
- if (rc)
- goto mdiobus_quit;
-
if (netif_msg_probe(priv)) {
dev_notice(emac_dev, "DaVinci EMAC Probe found device "\
"(regs: %p, irq: %d)\n",
@@ -2904,13 +1912,15 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
}
return 0;
-mdiobus_quit:
- mdiobus_free(emac_mii);
-
netdev_reg_err:
-mdio_alloc_err:
clk_disable(emac_clk);
no_irq_res:
+ if (priv->txchan)
+ cpdma_chan_destroy(priv->txchan);
+ if (priv->rxchan)
+ cpdma_chan_destroy(priv->rxchan);
+ cpdma_ctlr_destroy(priv->dma);
+no_dma:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, res->end - res->start + 1);
iounmap(priv->remap_addr);
@@ -2938,8 +1948,12 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mdiobus_unregister(priv->mii_bus);
- mdiobus_free(priv->mii_bus);
+
+ if (priv->txchan)
+ cpdma_chan_destroy(priv->txchan);
+ if (priv->rxchan)
+ cpdma_chan_destroy(priv->rxchan);
+ cpdma_ctlr_destroy(priv->dma);
release_mem_region(res->start, res->end - res->start + 1);
diff --git a/drivers/net/davinci_mdio.c b/drivers/net/davinci_mdio.c
new file mode 100644
index 000000000000..7615040df756
--- /dev/null
+++ b/drivers/net/davinci_mdio.c
@@ -0,0 +1,475 @@
+/*
+ * DaVinci MDIO Module driver
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * Shamelessly ripped out of davinci_emac.c, original copyrights follow:
+ *
+ * Copyright (C) 2009 Texas Instruments.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ---------------------------------------------------------------------------
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/phy.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/davinci_emac.h>
+
+/*
+ * This timeout definition is a worst-case ultra defensive measure against
+ * unexpected controller lock ups. Ideally, we should never ever hit this
+ * scenario in practice.
+ */
+#define MDIO_TIMEOUT 100 /* msecs */
+
+#define PHY_REG_MASK 0x1f
+#define PHY_ID_MASK 0x1f
+
+#define DEF_OUT_FREQ 2200000 /* 2.2 MHz */
+
+struct davinci_mdio_regs {
+ u32 version;
+ u32 control;
+#define CONTROL_IDLE BIT(31)
+#define CONTROL_ENABLE BIT(30)
+#define CONTROL_MAX_DIV (0xff)
+
+ u32 alive;
+ u32 link;
+ u32 linkintraw;
+ u32 linkintmasked;
+ u32 __reserved_0[2];
+ u32 userintraw;
+ u32 userintmasked;
+ u32 userintmaskset;
+ u32 userintmaskclr;
+ u32 __reserved_1[20];
+
+ struct {
+ u32 access;
+#define USERACCESS_GO BIT(31)
+#define USERACCESS_WRITE BIT(30)
+#define USERACCESS_ACK BIT(29)
+#define USERACCESS_READ (0)
+#define USERACCESS_DATA (0xffff)
+
+ u32 physel;
+ } user[0];
+};
+
+struct mdio_platform_data default_pdata = {
+ .bus_freq = DEF_OUT_FREQ,
+};
+
+struct davinci_mdio_data {
+ struct mdio_platform_data pdata;
+ struct davinci_mdio_regs __iomem *regs;
+ spinlock_t lock;
+ struct clk *clk;
+ struct device *dev;
+ struct mii_bus *bus;
+ bool suspended;
+ unsigned long access_time; /* jiffies */
+};
+
+static void __davinci_mdio_reset(struct davinci_mdio_data *data)
+{
+ u32 mdio_in, div, mdio_out_khz, access_time;
+
+ mdio_in = clk_get_rate(data->clk);
+ div = (mdio_in / data->pdata.bus_freq) - 1;
+ if (div > CONTROL_MAX_DIV)
+ div = CONTROL_MAX_DIV;
+
+ /* set enable and clock divider */
+ __raw_writel(div | CONTROL_ENABLE, &data->regs->control);
+
+ /*
+ * One mdio transaction consists of:
+ * 32 bits of preamble
+ * 32 bits of transferred data
+ * 24 bits of bus yield (not needed unless shared?)
+ */
+ mdio_out_khz = mdio_in / (1000 * (div + 1));
+ access_time = (88 * 1000) / mdio_out_khz;
+
+ /*
+ * In the worst case, we could be kicking off a user-access immediately
+ * after the mdio bus scan state-machine triggered its own read. If
+ * so, our request could get deferred by one access cycle. We
+ * defensively allow for 4 access cycles.
+ */
+ data->access_time = usecs_to_jiffies(access_time * 4);
+ if (!data->access_time)
+ data->access_time = 1;
+}
+
+static int davinci_mdio_reset(struct mii_bus *bus)
+{
+ struct davinci_mdio_data *data = bus->priv;
+ u32 phy_mask, ver;
+
+ __davinci_mdio_reset(data);
+
+ /* wait for scan logic to settle */
+ msleep(PHY_MAX_ADDR * data->access_time);
+
+ /* dump hardware version info */
+ ver = __raw_readl(&data->regs->version);
+ dev_info(data->dev, "davinci mdio revision %d.%d\n",
+ (ver >> 8) & 0xff, ver & 0xff);
+
+ /* get phy mask from the alive register */
+ phy_mask = __raw_readl(&data->regs->alive);
+ if (phy_mask) {
+ /* restrict mdio bus to live phys only */
+ dev_info(data->dev, "detected phy mask %x\n", ~phy_mask);
+ phy_mask = ~phy_mask;
+ } else {
+ /* desperately scan all phys */
+ dev_warn(data->dev, "no live phy, scanning all\n");
+ phy_mask = 0;
+ }
+ data->bus->phy_mask = phy_mask;
+
+ return 0;
+}
+
+/* wait until hardware is ready for another user access */
+static inline int wait_for_user_access(struct davinci_mdio_data *data)
+{
+ struct davinci_mdio_regs __iomem *regs = data->regs;
+ unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT);
+ u32 reg;
+
+ while (time_after(timeout, jiffies)) {
+ reg = __raw_readl(&regs->user[0].access);
+ if ((reg & USERACCESS_GO) == 0)
+ return 0;
+
+ reg = __raw_readl(&regs->control);
+ if ((reg & CONTROL_IDLE) == 0)
+ continue;
+
+ /*
+ * An emac soft_reset may have clobbered the mdio controller's
+ * state machine. We need to reset and retry the current
+ * operation
+ */
+ dev_warn(data->dev, "resetting idled controller\n");
+ __davinci_mdio_reset(data);
+ return -EAGAIN;
+ }
+ dev_err(data->dev, "timed out waiting for user access\n");
+ return -ETIMEDOUT;
+}
+
+/* wait until hardware state machine is idle */
+static inline int wait_for_idle(struct davinci_mdio_data *data)
+{
+ struct davinci_mdio_regs __iomem *regs = data->regs;
+ unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT);
+
+ while (time_after(timeout, jiffies)) {
+ if (__raw_readl(&regs->control) & CONTROL_IDLE)
+ return 0;
+ }
+ dev_err(data->dev, "timed out waiting for idle\n");
+ return -ETIMEDOUT;
+}
+
+static int davinci_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
+{
+ struct davinci_mdio_data *data = bus->priv;
+ u32 reg;
+ int ret;
+
+ if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+ return -EINVAL;
+
+ spin_lock(&data->lock);
+
+ if (data->suspended) {
+ spin_unlock(&data->lock);
+ return -ENODEV;
+ }
+
+ reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
+ (phy_id << 16));
+
+ while (1) {
+ ret = wait_for_user_access(data);
+ if (ret == -EAGAIN)
+ continue;
+ if (ret < 0)
+ break;
+
+ __raw_writel(reg, &data->regs->user[0].access);
+
+ ret = wait_for_user_access(data);
+ if (ret == -EAGAIN)
+ continue;
+ if (ret < 0)
+ break;
+
+ reg = __raw_readl(&data->regs->user[0].access);
+ ret = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -EIO;
+ break;
+ }
+
+ spin_unlock(&data->lock);
+
+ return ret;
+}
+
+static int davinci_mdio_write(struct mii_bus *bus, int phy_id,
+ int phy_reg, u16 phy_data)
+{
+ struct davinci_mdio_data *data = bus->priv;
+ u32 reg;
+ int ret;
+
+ if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+ return -EINVAL;
+
+ spin_lock(&data->lock);
+
+ if (data->suspended) {
+ spin_unlock(&data->lock);
+ return -ENODEV;
+ }
+
+ reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) |
+ (phy_id << 16) | (phy_data & USERACCESS_DATA));
+
+ while (1) {
+ ret = wait_for_user_access(data);
+ if (ret == -EAGAIN)
+ continue;
+ if (ret < 0)
+ break;
+
+ __raw_writel(reg, &data->regs->user[0].access);
+
+ ret = wait_for_user_access(data);
+ if (ret == -EAGAIN)
+ continue;
+ break;
+ }
+
+ spin_unlock(&data->lock);
+
+ return 0;
+}
+
+static int __devinit davinci_mdio_probe(struct platform_device *pdev)
+{
+ struct mdio_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct davinci_mdio_data *data;
+ struct resource *res;
+ struct phy_device *phy;
+ int ret, addr;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(dev, "failed to alloc device data\n");
+ return -ENOMEM;
+ }
+
+ data->pdata = pdata ? (*pdata) : default_pdata;
+
+ data->bus = mdiobus_alloc();
+ if (!data->bus) {
+ dev_err(dev, "failed to alloc mii bus\n");
+ ret = -ENOMEM;
+ goto bail_out;
+ }
+
+ data->bus->name = dev_name(dev);
+ data->bus->read = davinci_mdio_read,
+ data->bus->write = davinci_mdio_write,
+ data->bus->reset = davinci_mdio_reset,
+ data->bus->parent = dev;
+ data->bus->priv = data;
+ snprintf(data->bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+
+ data->clk = clk_get(dev, NULL);
+ if (IS_ERR(data->clk)) {
+ data->clk = NULL;
+ dev_err(dev, "failed to get device clock\n");
+ ret = PTR_ERR(data->clk);
+ goto bail_out;
+ }
+
+ clk_enable(data->clk);
+
+ dev_set_drvdata(dev, data);
+ data->dev = dev;
+ spin_lock_init(&data->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "could not find register map resource\n");
+ ret = -ENOENT;
+ goto bail_out;
+ }
+
+ res = devm_request_mem_region(dev, res->start, resource_size(res),
+ dev_name(dev));
+ if (!res) {
+ dev_err(dev, "could not allocate register map resource\n");
+ ret = -ENXIO;
+ goto bail_out;
+ }
+
+ data->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!data->regs) {
+ dev_err(dev, "could not map mdio registers\n");
+ ret = -ENOMEM;
+ goto bail_out;
+ }
+
+ /* register the mii bus */
+ ret = mdiobus_register(data->bus);
+ if (ret)
+ goto bail_out;
+
+ /* scan and dump the bus */
+ for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+ phy = data->bus->phy_map[addr];
+ if (phy) {
+ dev_info(dev, "phy[%d]: device %s, driver %s\n",
+ phy->addr, dev_name(&phy->dev),
+ phy->drv ? phy->drv->name : "unknown");
+ }
+ }
+
+ return 0;
+
+bail_out:
+ if (data->bus)
+ mdiobus_free(data->bus);
+
+ if (data->clk) {
+ clk_disable(data->clk);
+ clk_put(data->clk);
+ }
+
+ kfree(data);
+
+ return ret;
+}
+
+static int __devexit davinci_mdio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct davinci_mdio_data *data = dev_get_drvdata(dev);
+
+ if (data->bus)
+ mdiobus_free(data->bus);
+
+ if (data->clk) {
+ clk_disable(data->clk);
+ clk_put(data->clk);
+ }
+
+ dev_set_drvdata(dev, NULL);
+
+ kfree(data);
+
+ return 0;
+}
+
+static int davinci_mdio_suspend(struct device *dev)
+{
+ struct davinci_mdio_data *data = dev_get_drvdata(dev);
+ u32 ctrl;
+
+ spin_lock(&data->lock);
+
+ /* shutdown the scan state machine */
+ ctrl = __raw_readl(&data->regs->control);
+ ctrl &= ~CONTROL_ENABLE;
+ __raw_writel(ctrl, &data->regs->control);
+ wait_for_idle(data);
+
+ if (data->clk)
+ clk_disable(data->clk);
+
+ data->suspended = true;
+ spin_unlock(&data->lock);
+
+ return 0;
+}
+
+static int davinci_mdio_resume(struct device *dev)
+{
+ struct davinci_mdio_data *data = dev_get_drvdata(dev);
+ u32 ctrl;
+
+ spin_lock(&data->lock);
+ if (data->clk)
+ clk_enable(data->clk);
+
+ /* restart the scan state machine */
+ ctrl = __raw_readl(&data->regs->control);
+ ctrl |= CONTROL_ENABLE;
+ __raw_writel(ctrl, &data->regs->control);
+
+ data->suspended = false;
+ spin_unlock(&data->lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops davinci_mdio_pm_ops = {
+ .suspend = davinci_mdio_suspend,
+ .resume = davinci_mdio_resume,
+};
+
+static struct platform_driver davinci_mdio_driver = {
+ .driver = {
+ .name = "davinci_mdio",
+ .owner = THIS_MODULE,
+ .pm = &davinci_mdio_pm_ops,
+ },
+ .probe = davinci_mdio_probe,
+ .remove = __devexit_p(davinci_mdio_remove),
+};
+
+static int __init davinci_mdio_init(void)
+{
+ return platform_driver_register(&davinci_mdio_driver);
+}
+device_initcall(davinci_mdio_init);
+
+static void __exit davinci_mdio_exit(void)
+{
+ platform_driver_unregister(&davinci_mdio_driver);
+}
+module_exit(davinci_mdio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DaVinci MDIO driver");
diff --git a/drivers/net/mlx4/en_main.c b/drivers/net/mlx4/en_main.c
index 143906417048..f6e0d40cd876 100644
--- a/drivers/net/mlx4/en_main.c
+++ b/drivers/net/mlx4/en_main.c
@@ -124,6 +124,13 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
return 0;
}
+static void *mlx4_en_get_netdev(struct mlx4_dev *dev, void *ctx, u8 port)
+{
+ struct mlx4_en_dev *endev = ctx;
+
+ return endev->pndev[port];
+}
+
static void mlx4_en_event(struct mlx4_dev *dev, void *endev_ptr,
enum mlx4_dev_event event, int port)
{
@@ -282,9 +289,11 @@ err_free_res:
}
static struct mlx4_interface mlx4_en_interface = {
- .add = mlx4_en_add,
- .remove = mlx4_en_remove,
- .event = mlx4_en_event,
+ .add = mlx4_en_add,
+ .remove = mlx4_en_remove,
+ .event = mlx4_en_event,
+ .get_dev = mlx4_en_get_netdev,
+ .protocol = MLX4_PROTOCOL_EN,
};
static int __init mlx4_en_init(void)
diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c
index 79478bd4211a..6d6806b361e3 100644
--- a/drivers/net/mlx4/en_netdev.c
+++ b/drivers/net/mlx4/en_netdev.c
@@ -69,6 +69,7 @@ static void mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
int err;
+ int idx;
if (!priv->vlgrp)
return;
@@ -83,7 +84,10 @@ static void mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
if (err)
en_err(priv, "Failed configuring VLAN filter\n");
}
+ if (mlx4_register_vlan(mdev->dev, priv->port, vid, &idx))
+ en_err(priv, "failed adding vlan %d\n", vid);
mutex_unlock(&mdev->state_lock);
+
}
static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
@@ -91,6 +95,7 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
int err;
+ int idx;
if (!priv->vlgrp)
return;
@@ -101,6 +106,11 @@ static void mlx4_en_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
/* Remove VID from port VLAN filter */
mutex_lock(&mdev->state_lock);
+ if (!mlx4_find_cached_vlan(mdev->dev, priv->port, vid, &idx))
+ mlx4_unregister_vlan(mdev->dev, priv->port, idx);
+ else
+ en_err(priv, "could not find vid %d in cache\n", vid);
+
if (mdev->device_up && priv->port_up) {
err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, priv->vlgrp);
if (err)
diff --git a/drivers/net/mlx4/en_port.c b/drivers/net/mlx4/en_port.c
index aa3ef2aee5bf..7f5a3221e0c1 100644
--- a/drivers/net/mlx4/en_port.c
+++ b/drivers/net/mlx4/en_port.c
@@ -127,8 +127,8 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
memset(context, 0, sizeof *context);
context->base_qpn = cpu_to_be32(base_qpn);
- context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT | base_qpn);
- context->mcast = cpu_to_be32(1 << SET_PORT_PROMISC_SHIFT | base_qpn);
+ context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_EN_SHIFT | base_qpn);
+ context->mcast = cpu_to_be32(1 << SET_PORT_PROMISC_MODE_SHIFT | base_qpn);
context->intra_no_vlan = 0;
context->no_vlan = MLX4_NO_VLAN_IDX;
context->intra_vlan_miss = 0;
diff --git a/drivers/net/mlx4/en_port.h b/drivers/net/mlx4/en_port.h
index f6511aa2b7df..092e814b1981 100644
--- a/drivers/net/mlx4/en_port.h
+++ b/drivers/net/mlx4/en_port.h
@@ -36,7 +36,8 @@
#define SET_PORT_GEN_ALL_VALID 0x7
-#define SET_PORT_PROMISC_SHIFT 31
+#define SET_PORT_PROMISC_EN_SHIFT 31
+#define SET_PORT_PROMISC_MODE_SHIFT 30
enum {
MLX4_CMD_SET_VLAN_FLTR = 0x47,
diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c
index b716e1a1b298..b68eee2414c2 100644
--- a/drivers/net/mlx4/fw.c
+++ b/drivers/net/mlx4/fw.c
@@ -98,7 +98,8 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u32 flags)
[20] = "Address vector port checking support",
[21] = "UD multicast support",
[24] = "Demand paging support",
- [25] = "Router support"
+ [25] = "Router support",
+ [30] = "IBoE support"
};
int i;
diff --git a/drivers/net/mlx4/intf.c b/drivers/net/mlx4/intf.c
index 555067802751..73c94fcdfddf 100644
--- a/drivers/net/mlx4/intf.c
+++ b/drivers/net/mlx4/intf.c
@@ -161,3 +161,24 @@ void mlx4_unregister_device(struct mlx4_dev *dev)
mutex_unlock(&intf_mutex);
}
+
+void *mlx4_get_protocol_dev(struct mlx4_dev *dev, enum mlx4_protocol proto, int port)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ struct mlx4_device_context *dev_ctx;
+ unsigned long flags;
+ void *result = NULL;
+
+ spin_lock_irqsave(&priv->ctx_lock, flags);
+
+ list_for_each_entry(dev_ctx, &priv->ctx_list, list)
+ if (dev_ctx->intf->protocol == proto && dev_ctx->intf->get_dev) {
+ result = dev_ctx->intf->get_dev(dev, dev_ctx->context, port);
+ break;
+ }
+
+ spin_unlock_irqrestore(&priv->ctx_lock, flags);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(mlx4_get_protocol_dev);
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c
index 569fa3df381f..782f11d8fa71 100644
--- a/drivers/net/mlx4/main.c
+++ b/drivers/net/mlx4/main.c
@@ -103,7 +103,7 @@ MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports "
static int log_mtts_per_seg = ilog2(MLX4_MTT_ENTRY_PER_SEG);
module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444);
-MODULE_PARM_DESC(log_mtts_per_seg, "Log2 number of MTT entries per segment (1-5)");
+MODULE_PARM_DESC(log_mtts_per_seg, "Log2 number of MTT entries per segment (1-7)");
int mlx4_check_port_params(struct mlx4_dev *dev,
enum mlx4_port_type *port_type)
@@ -1310,7 +1310,7 @@ static int __init mlx4_verify_params(void)
return -1;
}
- if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 5)) {
+ if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) {
pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg);
return -1;
}
diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h
index 1fc16ab7ad2f..dfed6a07c2d7 100644
--- a/drivers/net/mlx4/mlx4_en.h
+++ b/drivers/net/mlx4/mlx4_en.h
@@ -475,6 +475,7 @@ struct mlx4_en_priv {
char *mc_addrs;
int mc_addrs_cnt;
struct mlx4_en_stat_out_mbox hw_stats;
+ int vids[128];
};
diff --git a/drivers/net/mlx4/port.c b/drivers/net/mlx4/port.c
index 8674ad5764c4..451339559bdc 100644
--- a/drivers/net/mlx4/port.c
+++ b/drivers/net/mlx4/port.c
@@ -188,6 +188,25 @@ static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, u8 port,
return err;
}
+int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx)
+{
+ struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
+ int i;
+
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; ++i) {
+ if (table->refs[i] &&
+ (vid == (MLX4_VLAN_MASK &
+ be32_to_cpu(table->entries[i])))) {
+ /* VLAN already registered, increase reference count */
+ *idx = i;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(mlx4_find_cached_vlan);
+
int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index)
{
struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
index f3f8be5a35fa..14f0955eca68 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/xilinx_emaclite.c
@@ -430,8 +430,8 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
}
/* Get the protocol type of the ethernet frame that arrived */
- proto_type = ((in_be32(addr + XEL_HEADER_OFFSET +
- XEL_RXBUFF_OFFSET) >> XEL_HEADER_SHIFT) &
+ proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET +
+ XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
XEL_RPLR_LENGTH_MASK);
/* Check if received ethernet frame is a raw ethernet frame
@@ -439,9 +439,9 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
if (proto_type == ETH_P_IP) {
- length = ((in_be32(addr +
+ length = ((ntohl(in_be32(addr +
XEL_HEADER_IP_LENGTH_OFFSET +
- XEL_RXBUFF_OFFSET) >>
+ XEL_RXBUFF_OFFSET)) >>
XEL_HEADER_SHIFT) &
XEL_RPLR_LENGTH_MASK);
length += ETH_HLEN + ETH_FCS_LEN;
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 6acbff389ab6..aa675ebd8eb3 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -4,7 +4,7 @@ config DTC
config OF
bool
-menu "Flattened Device Tree and Open Firmware support"
+menu "Device Tree and Open Firmware support"
depends on OF
config PROC_DEVICETREE
@@ -19,6 +19,9 @@ config OF_FLATTREE
bool
select DTC
+config OF_PROMTREE
+ bool
+
config OF_DYNAMIC
def_bool y
depends on PPC_OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 0052c405463a..7888155bea08 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,5 +1,6 @@
obj-y = base.o
obj-$(CONFIG_OF_FLATTREE) += fdt.o
+obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o
obj-$(CONFIG_OF_IRQ) += irq.o
obj-$(CONFIG_OF_DEVICE) += device.o platform.o
diff --git a/drivers/of/address.c b/drivers/of/address.c
index fcadb726d4f9..3a1c7e70b192 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -163,7 +163,7 @@ static int of_bus_pci_translate(u32 *addr, u64 offset, int na)
const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
unsigned int *flags)
{
- const u32 *prop;
+ const __be32 *prop;
unsigned int psize;
struct device_node *parent;
struct of_bus *bus;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index aa805250de76..710b53bfac6d 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -33,7 +33,7 @@ DEFINE_RWLOCK(devtree_lock);
int of_n_addr_cells(struct device_node *np)
{
- const int *ip;
+ const __be32 *ip;
do {
if (np->parent)
@@ -49,7 +49,7 @@ EXPORT_SYMBOL(of_n_addr_cells);
int of_n_size_cells(struct device_node *np)
{
- const int *ip;
+ const __be32 *ip;
do {
if (np->parent)
diff --git a/drivers/of/device.c b/drivers/of/device.c
index 92de0eb74aea..45d86530799f 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -81,29 +81,10 @@ struct device_attribute of_platform_device_attrs[] = {
__ATTR_NULL
};
-/**
- * of_release_dev - free an of device structure when all users of it are finished.
- * @dev: device that's been disconnected
- *
- * Will be called only by the device core when all users of this of device are
- * done.
- */
-void of_release_dev(struct device *dev)
-{
- struct platform_device *ofdev;
-
- ofdev = to_platform_device(dev);
- of_node_put(ofdev->dev.of_node);
- kfree(ofdev);
-}
-EXPORT_SYMBOL(of_release_dev);
-
-int of_device_register(struct platform_device *ofdev)
+int of_device_add(struct platform_device *ofdev)
{
BUG_ON(ofdev->dev.of_node == NULL);
- device_initialize(&ofdev->dev);
-
/* name and id have to be set so that the platform bus doesn't get
* confused on matching */
ofdev->name = dev_name(&ofdev->dev);
@@ -117,6 +98,12 @@ int of_device_register(struct platform_device *ofdev)
return device_add(&ofdev->dev);
}
+
+int of_device_register(struct platform_device *pdev)
+{
+ device_initialize(&pdev->dev);
+ return of_device_add(pdev);
+}
EXPORT_SYMBOL(of_device_register);
void of_device_unregister(struct platform_device *ofdev)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 65da5aec7552..c1360e02f921 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -533,8 +533,6 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#endif /* CONFIG_CMDLINE */
- early_init_dt_scan_chosen_arch(node);
-
pr_debug("Command line is: %s\n", cmd_line);
/* break now */
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 6e595e5a3977..75b0d3cb7676 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -24,6 +24,11 @@
#include <linux/of_irq.h>
#include <linux/string.h>
+/* For archs that don't support NO_IRQ (such as x86), provide a dummy value */
+#ifndef NO_IRQ
+#define NO_IRQ 0
+#endif
+
/**
* irq_of_parse_and_map - Parse and map an interrupt into linux virq space
* @device: Device node of the device whose interrupt is to be mapped
@@ -347,3 +352,37 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
return irq;
}
EXPORT_SYMBOL_GPL(of_irq_to_resource);
+
+/**
+ * of_irq_count - Count the number of IRQs a node uses
+ * @dev: pointer to device tree node
+ */
+int of_irq_count(struct device_node *dev)
+{
+ int nr = 0;
+
+ while (of_irq_to_resource(dev, nr, NULL) != NO_IRQ)
+ nr++;
+
+ return nr;
+}
+
+/**
+ * of_irq_to_resource_table - Fill in resource table with node's IRQ info
+ * @dev: pointer to device tree node
+ * @res: array of resources to fill in
+ * @nr_irqs: the number of IRQs (and upper bound for num of @res elements)
+ *
+ * Returns the size of the filled in table (up to @nr_irqs).
+ */
+int of_irq_to_resource_table(struct device_node *dev, struct resource *res,
+ int nr_irqs)
+{
+ int i;
+
+ for (i = 0; i < nr_irqs; i++, res++)
+ if (of_irq_to_resource(dev, i, res) == NO_IRQ)
+ break;
+
+ return i;
+}
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
index 0a694debd226..c85d3c7421fc 100644
--- a/drivers/of/of_i2c.c
+++ b/drivers/of/of_i2c.c
@@ -12,6 +12,7 @@
*/
#include <linux/i2c.h>
+#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_i2c.h>
#include <linux/of_irq.h>
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c
new file mode 100644
index 000000000000..28295d0a50f6
--- /dev/null
+++ b/drivers/of/pdt.c
@@ -0,0 +1,276 @@
+/* pdt.c: OF PROM device tree support code.
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ * {engebret|bergner}@us.ibm.com
+ *
+ * Adapted for sparc by David S. Miller davem@davemloft.net
+ * Adapted for multiple architectures by Andres Salomon <dilinger@queued.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_pdt.h>
+#include <asm/prom.h>
+
+static struct of_pdt_ops *of_pdt_prom_ops __initdata;
+
+void __initdata (*of_pdt_build_more)(struct device_node *dp,
+ struct device_node ***nextp);
+
+#if defined(CONFIG_SPARC)
+unsigned int of_pdt_unique_id __initdata;
+
+#define of_pdt_incr_unique_id(p) do { \
+ (p)->unique_id = of_pdt_unique_id++; \
+} while (0)
+
+static inline const char *of_pdt_node_name(struct device_node *dp)
+{
+ return dp->path_component_name;
+}
+
+#else
+
+static inline void of_pdt_incr_unique_id(void *p) { }
+static inline void irq_trans_init(struct device_node *dp) { }
+
+static inline const char *of_pdt_node_name(struct device_node *dp)
+{
+ return dp->name;
+}
+
+#endif /* !CONFIG_SPARC */
+
+static struct property * __init of_pdt_build_one_prop(phandle node, char *prev,
+ char *special_name,
+ void *special_val,
+ int special_len)
+{
+ static struct property *tmp = NULL;
+ struct property *p;
+ int err;
+
+ if (tmp) {
+ p = tmp;
+ memset(p, 0, sizeof(*p) + 32);
+ tmp = NULL;
+ } else {
+ p = prom_early_alloc(sizeof(struct property) + 32);
+ of_pdt_incr_unique_id(p);
+ }
+
+ p->name = (char *) (p + 1);
+ if (special_name) {
+ strcpy(p->name, special_name);
+ p->length = special_len;
+ p->value = prom_early_alloc(special_len);
+ memcpy(p->value, special_val, special_len);
+ } else {
+ err = of_pdt_prom_ops->nextprop(node, prev, p->name);
+ if (err) {
+ tmp = p;
+ return NULL;
+ }
+ p->length = of_pdt_prom_ops->getproplen(node, p->name);
+ if (p->length <= 0) {
+ p->length = 0;
+ } else {
+ int len;
+
+ p->value = prom_early_alloc(p->length + 1);
+ len = of_pdt_prom_ops->getproperty(node, p->name,
+ p->value, p->length);
+ if (len <= 0)
+ p->length = 0;
+ ((unsigned char *)p->value)[p->length] = '\0';
+ }
+ }
+ return p;
+}
+
+static struct property * __init of_pdt_build_prop_list(phandle node)
+{
+ struct property *head, *tail;
+
+ head = tail = of_pdt_build_one_prop(node, NULL,
+ ".node", &node, sizeof(node));
+
+ tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0);
+ tail = tail->next;
+ while(tail) {
+ tail->next = of_pdt_build_one_prop(node, tail->name,
+ NULL, NULL, 0);
+ tail = tail->next;
+ }
+
+ return head;
+}
+
+static char * __init of_pdt_get_one_property(phandle node, const char *name)
+{
+ char *buf = "<NULL>";
+ int len;
+
+ len = of_pdt_prom_ops->getproplen(node, name);
+ if (len > 0) {
+ buf = prom_early_alloc(len);
+ len = of_pdt_prom_ops->getproperty(node, name, buf, len);
+ }
+
+ return buf;
+}
+
+static char * __init of_pdt_try_pkg2path(phandle node)
+{
+ char *res, *buf = NULL;
+ int len;
+
+ if (!of_pdt_prom_ops->pkg2path)
+ return NULL;
+
+ if (of_pdt_prom_ops->pkg2path(node, buf, 0, &len))
+ return NULL;
+ buf = prom_early_alloc(len + 1);
+ if (of_pdt_prom_ops->pkg2path(node, buf, len, &len)) {
+ pr_err("%s: package-to-path failed\n", __func__);
+ return NULL;
+ }
+
+ res = strrchr(buf, '/');
+ if (!res) {
+ pr_err("%s: couldn't find / in %s\n", __func__, buf);
+ return NULL;
+ }
+ return res+1;
+}
+
+/*
+ * When fetching the node's name, first try using package-to-path; if
+ * that fails (either because the arch hasn't supplied a PROM callback,
+ * or some other random failure), fall back to just looking at the node's
+ * 'name' property.
+ */
+static char * __init of_pdt_build_name(phandle node)
+{
+ char *buf;
+
+ buf = of_pdt_try_pkg2path(node);
+ if (!buf)
+ buf = of_pdt_get_one_property(node, "name");
+
+ return buf;
+}
+
+static struct device_node * __init of_pdt_create_node(phandle node,
+ struct device_node *parent)
+{
+ struct device_node *dp;
+
+ if (!node)
+ return NULL;
+
+ dp = prom_early_alloc(sizeof(*dp));
+ of_pdt_incr_unique_id(dp);
+ dp->parent = parent;
+
+ kref_init(&dp->kref);
+
+ dp->name = of_pdt_build_name(node);
+ dp->type = of_pdt_get_one_property(node, "device_type");
+ dp->phandle = node;
+
+ dp->properties = of_pdt_build_prop_list(node);
+
+ irq_trans_init(dp);
+
+ return dp;
+}
+
+static char * __init of_pdt_build_full_name(struct device_node *dp)
+{
+ int len, ourlen, plen;
+ char *n;
+
+ plen = strlen(dp->parent->full_name);
+ ourlen = strlen(of_pdt_node_name(dp));
+ len = ourlen + plen + 2;
+
+ n = prom_early_alloc(len);
+ strcpy(n, dp->parent->full_name);
+ if (!of_node_is_root(dp->parent)) {
+ strcpy(n + plen, "/");
+ plen++;
+ }
+ strcpy(n + plen, of_pdt_node_name(dp));
+
+ return n;
+}
+
+static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
+ phandle node,
+ struct device_node ***nextp)
+{
+ struct device_node *ret = NULL, *prev_sibling = NULL;
+ struct device_node *dp;
+
+ while (1) {
+ dp = of_pdt_create_node(node, parent);
+ if (!dp)
+ break;
+
+ if (prev_sibling)
+ prev_sibling->sibling = dp;
+
+ if (!ret)
+ ret = dp;
+ prev_sibling = dp;
+
+ *(*nextp) = dp;
+ *nextp = &dp->allnext;
+
+#if defined(CONFIG_SPARC)
+ dp->path_component_name = build_path_component(dp);
+#endif
+ dp->full_name = of_pdt_build_full_name(dp);
+
+ dp->child = of_pdt_build_tree(dp,
+ of_pdt_prom_ops->getchild(node), nextp);
+
+ if (of_pdt_build_more)
+ of_pdt_build_more(dp, nextp);
+
+ node = of_pdt_prom_ops->getsibling(node);
+ }
+
+ return ret;
+}
+
+void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops)
+{
+ struct device_node **nextp;
+
+ BUG_ON(!ops);
+ of_pdt_prom_ops = ops;
+
+ allnodes = of_pdt_create_node(root_node, NULL);
+#if defined(CONFIG_SPARC)
+ allnodes->path_component_name = "";
+#endif
+ allnodes->full_name = "/";
+
+ nextp = &allnodes->allnext;
+ allnodes->child = of_pdt_build_tree(allnodes,
+ of_pdt_prom_ops->getchild(allnodes->phandle), &nextp);
+}
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index bb72223c22ae..5b4a07f1220e 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -584,34 +584,33 @@ struct platform_device *of_device_alloc(struct device_node *np,
struct device *parent)
{
struct platform_device *dev;
- int rc, i, num_reg = 0, num_irq = 0;
+ int rc, i, num_reg = 0, num_irq;
struct resource *res, temp_res;
- /* First count how many resources are needed */
- while (of_address_to_resource(np, num_reg, &temp_res) == 0)
- num_reg++;
- while (of_irq_to_resource(np, num_irq, &temp_res) != NO_IRQ)
- num_irq++;
-
- /* Allocate memory for both the struct device and the resource table */
- dev = kzalloc(sizeof(*dev) + (sizeof(*res) * (num_reg + num_irq)),
- GFP_KERNEL);
+ dev = platform_device_alloc("", -1);
if (!dev)
return NULL;
- res = (struct resource *) &dev[1];
+
+ /* count the io and irq resources */
+ while (of_address_to_resource(np, num_reg, &temp_res) == 0)
+ num_reg++;
+ num_irq = of_irq_count(np);
/* Populate the resource table */
if (num_irq || num_reg) {
+ res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
+ if (!res) {
+ platform_device_put(dev);
+ return NULL;
+ }
+
dev->num_resources = num_reg + num_irq;
dev->resource = res;
for (i = 0; i < num_reg; i++, res++) {
rc = of_address_to_resource(np, i, res);
WARN_ON(rc);
}
- for (i = 0; i < num_irq; i++, res++) {
- rc = of_irq_to_resource(np, i, res);
- WARN_ON(rc == NO_IRQ);
- }
+ WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
}
dev->dev.of_node = of_node_get(np);
@@ -619,7 +618,6 @@ struct platform_device *of_device_alloc(struct device_node *np,
dev->dev.dma_mask = &dev->archdata.dma_mask;
#endif
dev->dev.parent = parent;
- dev->dev.release = of_release_dev;
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
@@ -657,8 +655,8 @@ struct platform_device *of_platform_device_create(struct device_node *np,
* to do such, possibly using a device notifier
*/
- if (of_device_register(dev) != 0) {
- of_device_free(dev);
+ if (of_device_add(dev) != 0) {
+ platform_device_put(dev);
return NULL;
}
diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c
index 95f711b251ad..449de59bf35b 100644
--- a/drivers/oprofile/oprofilefs.c
+++ b/drivers/oprofile/oprofilefs.c
@@ -28,6 +28,7 @@ static struct inode *oprofilefs_get_inode(struct super_block *sb, int mode)
struct inode *inode = new_inode(sb);
if (inode) {
+ inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index cff7cc2c1f02..faec777b1ed4 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -92,6 +92,7 @@ config DELL_WMI
tristate "Dell WMI extras"
depends on ACPI_WMI
depends on INPUT
+ select INPUT_SPARSEKMAP
---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
@@ -140,6 +141,7 @@ config HP_WMI
depends on ACPI_WMI
depends on INPUT
depends on RFKILL || RFKILL = n
+ select INPUT_SPARSEKMAP
help
Say Y here if you want to support WMI-based hotkeys on HP laptops and
to read data from WMI such as docking or ambient light sensor state.
@@ -171,6 +173,7 @@ config PANASONIC_LAPTOP
tristate "Panasonic Laptop Extras"
depends on INPUT && ACPI
depends on BACKLIGHT_CLASS_DEVICE
+ select INPUT_SPARSEKMAP
---help---
This driver adds support for access to backlight control and hotkeys
on Panasonic Let's Note laptops.
@@ -219,8 +222,8 @@ config SONYPI_COMPAT
---help---
Build the sonypi driver compatibility code into the sony-laptop driver.
-config IDEAPAD_ACPI
- tristate "Lenovo IdeaPad ACPI Laptop Extras"
+config IDEAPAD_LAPTOP
+ tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI
depends on RFKILL
help
@@ -365,6 +368,26 @@ config THINKPAD_ACPI_HOTKEY_POLL
If you are not sure, say Y here. The driver enables polling only if
it is strictly necessary to do so.
+config SENSORS_HDAPS
+ tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
+ depends on INPUT && X86
+ select INPUT_POLLDEV
+ default n
+ help
+ This driver provides support for the IBM Hard Drive Active Protection
+ System (hdaps), which provides an accelerometer and other misc. data.
+ ThinkPads starting with the R50, T41, and X40 are supported. The
+ accelerometer data is readable via sysfs.
+
+ This driver also provides an absolute input class device, allowing
+ the laptop to act as a pinball machine-esque joystick.
+
+ If your ThinkPad is not recognized by the driver, please update to latest
+ BIOS. This is especially the case for some R52 ThinkPads.
+
+ Say Y here if you have an applicable laptop and want to experience
+ the awesome power of hdaps.
+
config INTEL_MENLOW
tristate "Thermal Management driver for Intel menlow platform"
depends on ACPI_THERMAL
@@ -478,6 +501,7 @@ config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras"
depends on ACPI
depends on INPUT
+ select INPUT_SPARSEKMAP
---help---
This driver adds support for hotkeys found on Topstar laptops.
@@ -492,6 +516,7 @@ config ACPI_TOSHIBA
depends on INPUT
depends on RFKILL || RFKILL = n
select INPUT_POLLDEV
+ select INPUT_SPARSEKMAP
---help---
This driver adds support for access to certain system settings
on "legacy free" Toshiba laptops. These laptops can be recognized by
@@ -590,4 +615,28 @@ config INTEL_IPS
functionality. If in doubt, say Y here; it will only load on
supported platforms.
+config IBM_RTL
+ tristate "Device driver to enable PRTL support"
+ depends on X86 && PCI
+ ---help---
+ Enable support for IBM Premium Real Time Mode (PRTM).
+ This module will allow you the enter and exit PRTM in the BIOS via
+ sysfs on platforms that support this feature. System in PRTM will
+ not receive CPU-generated SMIs for recoverable errors. Use of this
+ feature without proper support may void your hardware warranty.
+
+ If the proper BIOS support is found the driver will load and create
+ /sys/devices/system/ibm_rtl/. The "state" variable will indicate
+ whether or not the BIOS is in PRTM.
+ state = 0 (BIOS SMIs on)
+ state = 1 (BIOS SMIs off)
+
+config XO1_RFKILL
+ tristate "OLPC XO-1 software RF kill switch"
+ depends on OLPC
+ depends on RFKILL
+ ---help---
+ Support for enabling/disabling the WLAN interface on the OLPC XO-1
+ laptop.
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 85fb2b84f57e..9950ccc940b5 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -15,8 +15,9 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
-obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o
+obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
+obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
@@ -30,4 +31,5 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
-
+obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
+obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 2badee2fdeed..c8c65375bfe2 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void)
AMW0_find_mailled();
if (!interface) {
- printk(ACER_ERR "No or unsupported WMI interface, unable to "
+ printk(ACER_INFO "No or unsupported WMI interface, unable to "
"load\n");
return -ENODEV;
}
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index b756e07d41b4..60a5a5c6b50a 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -236,7 +236,6 @@ struct asus_laptop {
u8 light_level; /* light sensor level */
u8 light_switch; /* light sensor switch value */
u16 event_count[128]; /* count for each event TODO make this better */
- u16 *keycode_map;
};
static const struct key_entry asus_keymap[] = {
@@ -278,6 +277,7 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x99, { KEY_PHONE } },
{KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
+ {KE_KEY, 0xb5, { KEY_CALC } },
{KE_END, 0},
};
@@ -639,29 +639,29 @@ static int asus_backlight_notify(struct asus_laptop *asus)
static int asus_backlight_init(struct asus_laptop *asus)
{
struct backlight_device *bd;
- struct device *dev = &asus->platform_device->dev;
struct backlight_properties props;
- if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) &&
- !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) &&
- lcd_switch_handle) {
- memset(&props, 0, sizeof(struct backlight_properties));
- props.max_brightness = 15;
-
- bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
- asus, &asusbl_ops, &props);
- if (IS_ERR(bd)) {
- pr_err("Could not register asus backlight device\n");
- asus->backlight_device = NULL;
- return PTR_ERR(bd);
- }
+ if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
+ acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
+ !lcd_switch_handle)
+ return 0;
- asus->backlight_device = bd;
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = 15;
- bd->props.power = FB_BLANK_UNBLANK;
- bd->props.brightness = asus_read_brightness(bd);
- backlight_update_status(bd);
+ bd = backlight_device_register(ASUS_LAPTOP_FILE,
+ &asus->platform_device->dev, asus,
+ &asusbl_ops, &props);
+ if (IS_ERR(bd)) {
+ pr_err("Could not register asus backlight device\n");
+ asus->backlight_device = NULL;
+ return PTR_ERR(bd);
}
+
+ asus->backlight_device = bd;
+ bd->props.brightness = asus_read_brightness(bd);
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
return 0;
}
@@ -1065,9 +1065,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
*/
static int asus_gps_rfkill_set(void *data, bool blocked)
{
- acpi_handle handle = data;
+ struct asus_laptop *asus = data;
- return asus_gps_switch(handle, !blocked);
+ return asus_gps_switch(asus, !blocked);
}
static const struct rfkill_ops asus_gps_rfkill_ops = {
@@ -1094,7 +1094,7 @@ static int asus_rfkill_init(struct asus_laptop *asus)
asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
RFKILL_TYPE_GPS,
- &asus_gps_rfkill_ops, NULL);
+ &asus_gps_rfkill_ops, asus);
if (!asus->gps_rfkill)
return -EINVAL;
@@ -1130,7 +1130,6 @@ static int asus_input_init(struct asus_laptop *asus)
input->phys = ASUS_LAPTOP_FILE "/input0";
input->id.bustype = BUS_HOST;
input->dev.parent = &asus->platform_device->dev;
- input_set_drvdata(input, asus);
error = sparse_keymap_setup(input, asus_keymap, NULL);
if (error) {
@@ -1159,6 +1158,7 @@ static void asus_input_exit(struct asus_laptop *asus)
sparse_keymap_free(asus->inputdev);
input_unregister_device(asus->inputdev);
}
+ asus->inputdev = NULL;
}
/*
@@ -1200,111 +1200,100 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL);
static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan);
-static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth,
- store_bluetooth);
+static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
+ show_bluetooth, store_bluetooth);
static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp);
static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
-static void asus_sysfs_exit(struct asus_laptop *asus)
-{
- struct platform_device *device = asus->platform_device;
-
- device_remove_file(&device->dev, &dev_attr_infos);
- device_remove_file(&device->dev, &dev_attr_wlan);
- device_remove_file(&device->dev, &dev_attr_bluetooth);
- device_remove_file(&device->dev, &dev_attr_display);
- device_remove_file(&device->dev, &dev_attr_ledd);
- device_remove_file(&device->dev, &dev_attr_ls_switch);
- device_remove_file(&device->dev, &dev_attr_ls_level);
- device_remove_file(&device->dev, &dev_attr_gps);
-}
+static struct attribute *asus_attributes[] = {
+ &dev_attr_infos.attr,
+ &dev_attr_wlan.attr,
+ &dev_attr_bluetooth.attr,
+ &dev_attr_display.attr,
+ &dev_attr_ledd.attr,
+ &dev_attr_ls_level.attr,
+ &dev_attr_ls_switch.attr,
+ &dev_attr_gps.attr,
+ NULL
+};
-static int asus_sysfs_init(struct asus_laptop *asus)
+static mode_t asus_sysfs_is_visible(struct kobject *kobj,
+ struct attribute *attr,
+ int idx)
{
- struct platform_device *device = asus->platform_device;
- int err;
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct asus_laptop *asus = platform_get_drvdata(pdev);
+ acpi_handle handle = asus->handle;
+ bool supported;
- err = device_create_file(&device->dev, &dev_attr_infos);
- if (err)
- return err;
+ if (attr == &dev_attr_wlan.attr) {
+ supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
- if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) {
- err = device_create_file(&device->dev, &dev_attr_wlan);
- if (err)
- return err;
- }
+ } else if (attr == &dev_attr_bluetooth.attr) {
+ supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
- if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) {
- err = device_create_file(&device->dev, &dev_attr_bluetooth);
- if (err)
- return err;
- }
+ } else if (attr == &dev_attr_display.attr) {
+ supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
- if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) {
- err = device_create_file(&device->dev, &dev_attr_display);
- if (err)
- return err;
- }
+ } else if (attr == &dev_attr_ledd.attr) {
+ supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
- if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) {
- err = device_create_file(&device->dev, &dev_attr_ledd);
- if (err)
- return err;
- }
-
- if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
- !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
- err = device_create_file(&device->dev, &dev_attr_ls_switch);
- if (err)
- return err;
- err = device_create_file(&device->dev, &dev_attr_ls_level);
- if (err)
- return err;
- }
+ } else if (attr == &dev_attr_ls_switch.attr ||
+ attr == &dev_attr_ls_level.attr) {
+ supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
+ !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
- 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)) {
- err = device_create_file(&device->dev, &dev_attr_gps);
- if (err)
- return err;
+ } else if (attr == &dev_attr_gps.attr) {
+ supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
+ !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
+ !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL);
+ } else {
+ supported = true;
}
- return err;
+ return supported ? attr->mode : 0;
}
+
+static const struct attribute_group asus_attr_group = {
+ .is_visible = asus_sysfs_is_visible,
+ .attrs = asus_attributes,
+};
+
static int asus_platform_init(struct asus_laptop *asus)
{
- int err;
+ int result;
asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1);
if (!asus->platform_device)
return -ENOMEM;
platform_set_drvdata(asus->platform_device, asus);
- err = platform_device_add(asus->platform_device);
- if (err)
+ result = platform_device_add(asus->platform_device);
+ if (result)
goto fail_platform_device;
- err = asus_sysfs_init(asus);
- if (err)
+ result = sysfs_create_group(&asus->platform_device->dev.kobj,
+ &asus_attr_group);
+ if (result)
goto fail_sysfs;
+
return 0;
fail_sysfs:
- asus_sysfs_exit(asus);
platform_device_del(asus->platform_device);
fail_platform_device:
platform_device_put(asus->platform_device);
- return err;
+ return result;
}
static void asus_platform_exit(struct asus_laptop *asus)
{
- asus_sysfs_exit(asus);
+ sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group);
platform_device_unregister(asus->platform_device);
}
@@ -1428,8 +1417,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
return AE_OK;
}
-static bool asus_device_present;
-
static int __devinit asus_acpi_init(struct asus_laptop *asus)
{
int result = 0;
@@ -1474,6 +1461,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
return result;
}
+static bool asus_device_present;
+
static int __devinit asus_acpi_add(struct acpi_device *device)
{
struct asus_laptop *asus;
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 4413975912e0..cf8a89a0d8f5 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -25,6 +25,8 @@
#include <linux/mm.h>
#include <linux/i8042.h>
#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include "../../firmware/dcdbas.h"
#define BRIGHTNESS_TOKEN 0x7d
@@ -325,6 +327,75 @@ static const struct rfkill_ops dell_rfkill_ops = {
.query = dell_rfkill_query,
};
+static struct dentry *dell_laptop_dir;
+
+static int dell_debugfs_show(struct seq_file *s, void *data)
+{
+ int status;
+
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+ status = buffer->output[1];
+ release_buffer();
+
+ seq_printf(s, "status:\t0x%X\n", status);
+ seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
+ status & BIT(0));
+ seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
+ (status & BIT(1)) >> 1);
+ seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
+ (status & BIT(2)) >> 2);
+ seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
+ (status & BIT(3)) >> 3);
+ seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
+ (status & BIT(4)) >> 4);
+ seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
+ (status & BIT(5)) >> 5);
+ seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
+ (status & BIT(8)) >> 8);
+ seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
+ (status & BIT(9)) >> 9);
+ seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
+ (status & BIT(10)) >> 10);
+ seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
+ (status & BIT(16)) >> 16);
+ seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
+ (status & BIT(17)) >> 17);
+ seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
+ (status & BIT(18)) >> 18);
+ seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
+ (status & BIT(19)) >> 19);
+
+ seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
+ seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
+ hwswitch_state & BIT(0));
+ seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
+ (hwswitch_state & BIT(1)) >> 1);
+ seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
+ (hwswitch_state & BIT(2)) >> 2);
+ seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
+ (hwswitch_state & BIT(7)) >> 7);
+ seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
+ (hwswitch_state & BIT(8)) >> 8);
+ seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
+ (hwswitch_state & BIT(15)) >> 15);
+
+ return 0;
+}
+
+static int dell_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dell_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations dell_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = dell_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static void dell_update_rfkill(struct work_struct *ignored)
{
if (wifi_rfkill)
@@ -556,6 +627,11 @@ static int __init dell_init(void)
goto fail_filter;
}
+ dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
+ if (dell_laptop_dir != NULL)
+ debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
+ &dell_debugfs_fops);
+
#ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't
* register the platform controller.
@@ -615,6 +691,7 @@ fail_platform_driver:
static void __exit dell_exit(void)
{
+ debugfs_remove_recursive(dell_laptop_dir);
i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device);
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 08fb70f6d9bf..77f1d55414c6 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#include <acpi/acpi_drivers.h>
#include <linux/acpi.h>
#include <linux/string.h>
@@ -44,78 +45,70 @@ static int acpi_video;
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
-struct key_entry {
- char type; /* See KE_* below */
- u16 code;
- u16 keycode;
-};
-
-enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
-
/*
* Certain keys are flagged as KE_IGNORE. All of these are either
* notifications (rather than requests for change) or are also sent
* via the keyboard controller so should not be sent again.
*/
-static struct key_entry dell_legacy_wmi_keymap[] = {
- {KE_KEY, 0xe045, KEY_PROG1},
- {KE_KEY, 0xe009, KEY_EJECTCD},
+static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
+ { KE_KEY, 0xe045, { KEY_PROG1 } },
+ { KE_KEY, 0xe009, { KEY_EJECTCD } },
/* These also contain the brightness level at offset 6 */
- {KE_KEY, 0xe006, KEY_BRIGHTNESSUP},
- {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN},
+ { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
/* Battery health status button */
- {KE_KEY, 0xe007, KEY_BATTERY},
+ { KE_KEY, 0xe007, { KEY_BATTERY } },
/* This is actually for all radios. Although physically a
* switch, the notification does not provide an indication of
* state and so it should be reported as a key */
- {KE_KEY, 0xe008, KEY_WLAN},
+ { KE_KEY, 0xe008, { KEY_WLAN } },
/* The next device is at offset 6, the active devices are at
offset 8 and the attached devices at offset 10 */
- {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE},
+ { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
- {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
+ { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
/* BIOS error detected */
- {KE_IGNORE, 0xe00d, KEY_RESERVED},
+ { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
/* Wifi Catcher */
- {KE_KEY, 0xe011, KEY_PROG2},
+ { KE_KEY, 0xe011, {KEY_PROG2 } },
/* Ambient light sensor toggle */
- {KE_IGNORE, 0xe013, KEY_RESERVED},
-
- {KE_IGNORE, 0xe020, KEY_MUTE},
- {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN},
- {KE_IGNORE, 0xe030, KEY_VOLUMEUP},
- {KE_IGNORE, 0xe033, KEY_KBDILLUMUP},
- {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN},
- {KE_IGNORE, 0xe03a, KEY_CAPSLOCK},
- {KE_IGNORE, 0xe045, KEY_NUMLOCK},
- {KE_IGNORE, 0xe046, KEY_SCROLLLOCK},
- {KE_END, 0}
+ { KE_IGNORE, 0xe013, { KEY_RESERVED } },
+
+ { KE_IGNORE, 0xe020, { KEY_MUTE } },
+ { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
+ { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
+ { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
+ { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
+ { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
+ { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
+ { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
+ { KE_END, 0 }
};
static bool dell_new_hk_type;
-struct dell_new_keymap_entry {
+struct dell_bios_keymap_entry {
u16 scancode;
u16 keycode;
};
-struct dell_hotkey_table {
+struct dell_bios_hotkey_table {
struct dmi_header header;
- struct dell_new_keymap_entry keymap[];
+ struct dell_bios_keymap_entry keymap[];
};
-static struct key_entry *dell_new_wmi_keymap;
+static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
-static u16 bios_to_linux_keycode[256] = {
+static const u16 bios_to_linux_keycode[256] __initconst = {
KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
@@ -138,68 +131,11 @@ static u16 bios_to_linux_keycode[256] = {
KEY_PROG3
};
-
-static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap;
-
static struct input_dev *dell_wmi_input_dev;
-static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code)
-{
- struct key_entry *key;
-
- for (key = dell_wmi_keymap; key->type != KE_END; key++)
- if (code == key->code)
- return key;
-
- return NULL;
-}
-
-static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode)
-{
- struct key_entry *key;
-
- for (key = dell_wmi_keymap; key->type != KE_END; key++)
- if (key->type == KE_KEY && keycode == key->keycode)
- return key;
-
- return NULL;
-}
-
-static int dell_wmi_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
-{
- struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode);
-
- if (key && key->type == KE_KEY) {
- *keycode = key->keycode;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int dell_wmi_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
-{
- struct key_entry *key;
- unsigned int old_keycode;
-
- key = dell_wmi_get_entry_by_scancode(scancode);
- if (key && key->type == KE_KEY) {
- old_keycode = key->keycode;
- key->keycode = keycode;
- set_bit(keycode, dev->keybit);
- if (!dell_wmi_get_entry_by_keycode(old_keycode))
- clear_bit(old_keycode, dev->keybit);
- return 0;
- }
- return -EINVAL;
-}
-
static void dell_wmi_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
- static struct key_entry *key;
union acpi_object *obj;
acpi_status status;
@@ -212,8 +148,10 @@ static void dell_wmi_notify(u32 value, void *context)
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_BUFFER) {
+ const struct key_entry *key;
int reported_key;
u16 *buffer_entry = (u16 *)obj->buffer.pointer;
+
if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
printk(KERN_INFO "dell-wmi: Received unknown WMI event"
" (0x%x)\n", buffer_entry[1]);
@@ -226,8 +164,8 @@ static void dell_wmi_notify(u32 value, void *context)
else
reported_key = (int)buffer_entry[1] & 0xffff;
- key = dell_wmi_get_entry_by_scancode(reported_key);
-
+ key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
+ reported_key);
if (!key) {
printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
reported_key);
@@ -237,92 +175,98 @@ static void dell_wmi_notify(u32 value, void *context)
* come via ACPI */
;
} else {
- input_report_key(dell_wmi_input_dev, key->keycode, 1);
- input_sync(dell_wmi_input_dev);
- input_report_key(dell_wmi_input_dev, key->keycode, 0);
- input_sync(dell_wmi_input_dev);
+ sparse_keymap_report_entry(dell_wmi_input_dev, key,
+ 1, true);
}
}
kfree(obj);
}
-
-static void setup_new_hk_map(const struct dmi_header *dm)
+static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
{
-
+ int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
+ sizeof(struct dell_bios_keymap_entry);
+ struct key_entry *keymap;
int i;
- int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry);
- struct dell_hotkey_table *table =
- container_of(dm, struct dell_hotkey_table, header);
- dell_new_wmi_keymap = kzalloc((hotkey_num+1) *
- sizeof(struct key_entry), GFP_KERNEL);
+ keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
+ if (!keymap)
+ return NULL;
for (i = 0; i < hotkey_num; i++) {
- dell_new_wmi_keymap[i].type = KE_KEY;
- dell_new_wmi_keymap[i].code = table->keymap[i].scancode;
- dell_new_wmi_keymap[i].keycode =
- (table->keymap[i].keycode > 255) ? 0 :
- bios_to_linux_keycode[table->keymap[i].keycode];
+ const struct dell_bios_keymap_entry *bios_entry =
+ &dell_bios_hotkey_table->keymap[i];
+ keymap[i].type = KE_KEY;
+ keymap[i].code = bios_entry->scancode;
+ keymap[i].keycode = bios_entry->keycode < 256 ?
+ bios_to_linux_keycode[bios_entry->keycode] :
+ KEY_RESERVED;
}
- dell_new_wmi_keymap[i].type = KE_END;
- dell_new_wmi_keymap[i].code = 0;
- dell_new_wmi_keymap[i].keycode = 0;
-
- dell_wmi_keymap = dell_new_wmi_keymap;
+ keymap[hotkey_num].type = KE_END;
+ return keymap;
}
-
-static void find_hk_type(const struct dmi_header *dm, void *dummy)
-{
-
- if ((dm->type == 0xb2) && (dm->length > 6)) {
- dell_new_hk_type = true;
- setup_new_hk_map(dm);
- }
-
-}
-
-
static int __init dell_wmi_input_setup(void)
{
- struct key_entry *key;
int err;
dell_wmi_input_dev = input_allocate_device();
-
if (!dell_wmi_input_dev)
return -ENOMEM;
dell_wmi_input_dev->name = "Dell WMI hotkeys";
dell_wmi_input_dev->phys = "wmi/input0";
dell_wmi_input_dev->id.bustype = BUS_HOST;
- dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode;
- dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode;
-
- for (key = dell_wmi_keymap; key->type != KE_END; key++) {
- switch (key->type) {
- case KE_KEY:
- set_bit(EV_KEY, dell_wmi_input_dev->evbit);
- set_bit(key->keycode, dell_wmi_input_dev->keybit);
- break;
- case KE_SW:
- set_bit(EV_SW, dell_wmi_input_dev->evbit);
- set_bit(key->keycode, dell_wmi_input_dev->swbit);
- break;
+
+ if (dell_new_hk_type) {
+ const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
+ if (!keymap) {
+ err = -ENOMEM;
+ goto err_free_dev;
}
- }
- err = input_register_device(dell_wmi_input_dev);
+ err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
- if (err) {
- input_free_device(dell_wmi_input_dev);
- return err;
+ /*
+ * Sparse keymap library makes a copy of keymap so we
+ * don't need the original one that was allocated.
+ */
+ kfree(keymap);
+ } else {
+ err = sparse_keymap_setup(dell_wmi_input_dev,
+ dell_wmi_legacy_keymap, NULL);
}
+ if (err)
+ goto err_free_dev;
+
+ err = input_register_device(dell_wmi_input_dev);
+ if (err)
+ goto err_free_keymap;
return 0;
+
+ err_free_keymap:
+ sparse_keymap_free(dell_wmi_input_dev);
+ err_free_dev:
+ input_free_device(dell_wmi_input_dev);
+ return err;
+}
+
+static void dell_wmi_input_destroy(void)
+{
+ sparse_keymap_free(dell_wmi_input_dev);
+ input_unregister_device(dell_wmi_input_dev);
+}
+
+static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
+{
+ if (dm->type == 0xb2 && dm->length > 6) {
+ dell_new_hk_type = true;
+ dell_bios_hotkey_table =
+ container_of(dm, struct dell_bios_hotkey_table, header);
+ }
}
static int __init dell_wmi_init(void)
@@ -339,18 +283,13 @@ static int __init dell_wmi_init(void)
acpi_video = acpi_video_backlight_support();
err = dell_wmi_input_setup();
- if (err) {
- if (dell_new_hk_type)
- kfree(dell_wmi_keymap);
+ if (err)
return err;
- }
status = wmi_install_notify_handler(DELL_EVENT_GUID,
dell_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
- input_unregister_device(dell_wmi_input_dev);
- if (dell_new_hk_type)
- kfree(dell_wmi_keymap);
+ dell_wmi_input_destroy();
printk(KERN_ERR
"dell-wmi: Unable to register notify handler - %d\n",
status);
@@ -359,14 +298,11 @@ static int __init dell_wmi_init(void)
return 0;
}
+module_init(dell_wmi_init);
static void __exit dell_wmi_exit(void)
{
wmi_remove_notify_handler(DELL_EVENT_GUID);
- input_unregister_device(dell_wmi_input_dev);
- if (dell_new_hk_type)
- kfree(dell_wmi_keymap);
+ dell_wmi_input_destroy();
}
-
-module_init(dell_wmi_init);
module_exit(dell_wmi_exit);
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 6b8e06206c46..b2edfdcdcb84 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -165,6 +165,7 @@ struct eeepc_laptop {
u16 event_count[128]; /* count for each event */
struct platform_device *platform_device;
+ struct acpi_device *device; /* the device we are in */
struct device *hwmon_device;
struct backlight_device *backlight_device;
@@ -1193,9 +1194,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
eeepc->inputdev = input;
return 0;
- err_free_keymap:
+err_free_keymap:
sparse_keymap_free(input);
- err_free_dev:
+err_free_dev:
input_free_device(input);
return error;
}
@@ -1206,6 +1207,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
sparse_keymap_free(eeepc->inputdev);
input_unregister_device(eeepc->inputdev);
}
+ eeepc->inputdev = NULL;
}
/*
@@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)
cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
}
-static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
- struct acpi_device *device)
+static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
{
unsigned int init_flags;
int result;
- result = acpi_bus_get_status(device);
+ result = acpi_bus_get_status(eeepc->device);
if (result)
return result;
- if (!device->status.present) {
+ if (!eeepc->device->status.present) {
pr_err("Hotkey device not present, aborting\n");
return -ENODEV;
}
@@ -1384,12 +1385,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device)
strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
device->driver_data = eeepc;
+ eeepc->device = device;
eeepc->hotplug_disabled = hotplug_disabled;
eeepc_dmi_check(eeepc);
- result = eeepc_acpi_init(eeepc, device);
+ result = eeepc_acpi_init(eeepc);
if (result)
goto fail_platform;
eeepc_enable_camera(eeepc);
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 9dc50fbf3d0b..462ceab93f87 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
#define EEEPC_WMI_METHODID_DEVS 0x53564544
#define EEEPC_WMI_METHODID_DSTS 0x53544344
+#define EEEPC_WMI_METHODID_CFVS 0x53564643
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
@@ -69,6 +70,11 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
{ KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
+ { KE_KEY, 0xe1, { KEY_F14 } },
+ { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
+ { KE_KEY, 0xe0, { KEY_PROG1 } },
+ { KE_KEY, 0x5c, { KEY_F15 } },
{ KE_END, 0},
};
@@ -292,6 +298,49 @@ static void eeepc_wmi_notify(u32 value, void *context)
kfree(obj);
}
+static int store_cpufv(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
+ acpi_status status;
+
+ if (!count || sscanf(buf, "%i", &value) != 1)
+ return -EINVAL;
+ if (value < 0 || value > 2)
+ return -EINVAL;
+
+ status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
+ 1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ else
+ return count;
+}
+
+static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
+
+static void eeepc_wmi_sysfs_exit(struct platform_device *device)
+{
+ device_remove_file(&device->dev, &dev_attr_cpufv);
+}
+
+static int eeepc_wmi_sysfs_init(struct platform_device *device)
+{
+ int retval = -ENOMEM;
+
+ retval = device_create_file(&device->dev, &dev_attr_cpufv);
+ if (retval)
+ goto error_sysfs;
+
+ return 0;
+
+error_sysfs:
+ eeepc_wmi_sysfs_exit(platform_device);
+ return retval;
+}
+
static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
{
struct eeepc_wmi *eeepc;
@@ -387,8 +436,14 @@ static int __init eeepc_wmi_init(void)
goto del_dev;
}
+ err = eeepc_wmi_sysfs_init(platform_device);
+ if (err)
+ goto del_sysfs;
+
return 0;
+del_sysfs:
+ eeepc_wmi_sysfs_exit(platform_device);
del_dev:
platform_device_del(platform_device);
put_dev:
@@ -403,6 +458,7 @@ static void __exit eeepc_wmi_exit(void)
{
struct eeepc_wmi *eeepc;
+ eeepc_wmi_sysfs_exit(platform_device);
eeepc = platform_get_drvdata(platform_device);
platform_driver_unregister(&platform_driver);
platform_device_unregister(platform_device);
diff --git a/drivers/hwmon/hdaps.c b/drivers/platform/x86/hdaps.c
index bfd42f18924b..067bf36d32f3 100644
--- a/drivers/hwmon/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -1,5 +1,5 @@
/*
- * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System
+ * hdaps.c - driver for IBM's Hard Drive Active Protection System
*
* Copyright (C) 2005 Robert Love <rml@novell.com>
* Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index c1741142a4cb..1dac659b5e0c 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/rfkill.h>
@@ -88,24 +89,16 @@ struct bios_return {
u32 value;
};
-struct key_entry {
- char type; /* See KE_* below */
- u16 code;
- u16 keycode;
-};
-
-enum { KE_KEY, KE_END };
-
-static struct key_entry hp_wmi_keymap[] = {
- {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
- {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
- {KE_KEY, 0x20e6, KEY_PROG1},
- {KE_KEY, 0x20e8, KEY_MEDIA},
- {KE_KEY, 0x2142, KEY_MEDIA},
- {KE_KEY, 0x213b, KEY_INFO},
- {KE_KEY, 0x2169, KEY_DIRECTION},
- {KE_KEY, 0x231b, KEY_HELP},
- {KE_END, 0}
+static const struct key_entry hp_wmi_keymap[] = {
+ { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, 0x20e6, { KEY_PROG1 } },
+ { KE_KEY, 0x20e8, { KEY_MEDIA } },
+ { KE_KEY, 0x2142, { KEY_MEDIA } },
+ { KE_KEY, 0x213b, { KEY_INFO } },
+ { KE_KEY, 0x2169, { KEY_DIRECTION } },
+ { KE_KEY, 0x231b, { KEY_HELP } },
+ { KE_END, 0 }
};
static struct input_dev *hp_wmi_input_dev;
@@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
-static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code)
-{
- struct key_entry *key;
-
- for (key = hp_wmi_keymap; key->type != KE_END; key++)
- if (code == key->code)
- return key;
-
- return NULL;
-}
-
-static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode)
-{
- struct key_entry *key;
-
- for (key = hp_wmi_keymap; key->type != KE_END; key++)
- if (key->type == KE_KEY && keycode == key->keycode)
- return key;
-
- return NULL;
-}
-
-static int hp_wmi_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
-{
- struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
-
- if (key && key->type == KE_KEY) {
- *keycode = key->keycode;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int hp_wmi_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
-{
- struct key_entry *key;
- unsigned int old_keycode;
-
- key = hp_wmi_get_entry_by_scancode(scancode);
- if (key && key->type == KE_KEY) {
- old_keycode = key->keycode;
- key->keycode = keycode;
- set_bit(keycode, dev->keybit);
- if (!hp_wmi_get_entry_by_keycode(old_keycode))
- clear_bit(old_keycode, dev->keybit);
- return 0;
- }
-
- return -EINVAL;
-}
-
static void hp_wmi_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
- static struct key_entry *key;
union acpi_object *obj;
u32 event_id, event_data;
int key_code = 0, ret;
@@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context)
sizeof(key_code));
if (ret)
break;
- key = hp_wmi_get_entry_by_scancode(key_code);
- if (key) {
- switch (key->type) {
- case KE_KEY:
- input_report_key(hp_wmi_input_dev,
- key->keycode, 1);
- input_sync(hp_wmi_input_dev);
- input_report_key(hp_wmi_input_dev,
- key->keycode, 0);
- input_sync(hp_wmi_input_dev);
- break;
- }
- } else
+
+ if (!sparse_keymap_report_event(hp_wmi_input_dev,
+ key_code, 1, true))
printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
key_code);
break;
@@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context)
static int __init hp_wmi_input_setup(void)
{
- struct key_entry *key;
+ acpi_status status;
int err;
hp_wmi_input_dev = input_allocate_device();
@@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_input_dev->name = "HP WMI hotkeys";
hp_wmi_input_dev->phys = "wmi/input0";
hp_wmi_input_dev->id.bustype = BUS_HOST;
- hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
- hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
-
- for (key = hp_wmi_keymap; key->type != KE_END; key++) {
- switch (key->type) {
- case KE_KEY:
- set_bit(EV_KEY, hp_wmi_input_dev->evbit);
- set_bit(key->keycode, hp_wmi_input_dev->keybit);
- break;
- }
- }
- set_bit(EV_SW, hp_wmi_input_dev->evbit);
- set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
- set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
+ __set_bit(EV_SW, hp_wmi_input_dev->evbit);
+ __set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
+ __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
+
+ err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
+ if (err)
+ goto err_free_dev;
/* Set initial hardware state */
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
@@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
- err = input_register_device(hp_wmi_input_dev);
-
- if (err) {
- input_free_device(hp_wmi_input_dev);
- return err;
+ status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
+ if (ACPI_FAILURE(status)) {
+ err = -EIO;
+ goto err_free_keymap;
}
+ err = input_register_device(hp_wmi_input_dev);
+ if (err)
+ goto err_uninstall_notifier;
+
return 0;
+
+ err_uninstall_notifier:
+ wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+ err_free_keymap:
+ sparse_keymap_free(hp_wmi_input_dev);
+ err_free_dev:
+ input_free_device(hp_wmi_input_dev);
+ return err;
+}
+
+static void hp_wmi_input_destroy(void)
+{
+ wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+ sparse_keymap_free(hp_wmi_input_dev);
+ input_unregister_device(hp_wmi_input_dev);
}
static void cleanup_sysfs(struct platform_device *device)
@@ -704,15 +643,9 @@ static int __init hp_wmi_init(void)
int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
if (event_capable) {
- err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
- hp_wmi_notify, NULL);
- if (ACPI_FAILURE(err))
- return -EINVAL;
err = hp_wmi_input_setup();
- if (err) {
- wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+ if (err)
return err;
- }
}
if (bios_capable) {
@@ -739,20 +672,17 @@ err_device_add:
err_device_alloc:
platform_driver_unregister(&hp_wmi_driver);
err_driver_reg:
- if (wmi_has_guid(HPWMI_EVENT_GUID)) {
- input_unregister_device(hp_wmi_input_dev);
- wmi_remove_notify_handler(HPWMI_EVENT_GUID);
- }
+ if (event_capable)
+ hp_wmi_input_destroy();
return err;
}
static void __exit hp_wmi_exit(void)
{
- if (wmi_has_guid(HPWMI_EVENT_GUID)) {
- wmi_remove_notify_handler(HPWMI_EVENT_GUID);
- input_unregister_device(hp_wmi_input_dev);
- }
+ if (wmi_has_guid(HPWMI_EVENT_GUID))
+ hp_wmi_input_destroy();
+
if (hp_wmi_platform_dev) {
platform_device_unregister(hp_wmi_platform_dev);
platform_driver_unregister(&hp_wmi_driver);
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c
new file mode 100644
index 000000000000..3c2c6b91ecb3
--- /dev/null
+++ b/drivers/platform/x86/ibm_rtl.c
@@ -0,0 +1,341 @@
+/*
+ * IBM Real-Time Linux driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2010
+ *
+ * Author: Keith Mannthey <kmannth@us.ibm.com>
+ * Vernon Mauery <vernux@us.ibm.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/sysdev.h>
+#include <linux/dmi.h>
+#include <linux/mutex.h>
+#include <asm/bios_ebda.h>
+
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
+
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Show debug output");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");
+MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");
+
+#define RTL_ADDR_TYPE_IO 1
+#define RTL_ADDR_TYPE_MMIO 2
+
+#define RTL_CMD_ENTER_PRTM 1
+#define RTL_CMD_EXIT_PRTM 2
+
+/* The RTL table as presented by the EBDA: */
+struct ibm_rtl_table {
+ char signature[5]; /* signature should be "_RTL_" */
+ u8 version;
+ u8 rt_status;
+ u8 command;
+ u8 command_status;
+ u8 cmd_address_type;
+ u8 cmd_granularity;
+ u8 cmd_offset;
+ u16 reserve1;
+ u32 cmd_port_address; /* platform dependent address */
+ u32 cmd_port_value; /* platform dependent value */
+} __attribute__((packed));
+
+/* to locate "_RTL_" signature do a masked 5-byte integer compare */
+#define RTL_SIGNATURE 0x0000005f4c54525fULL
+#define RTL_MASK 0x000000ffffffffffULL
+
+#define RTL_DEBUG(A, ...) do { \
+ if (debug) \
+ pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \
+} while (0)
+
+static DEFINE_MUTEX(rtl_lock);
+static struct ibm_rtl_table __iomem *rtl_table;
+static void __iomem *ebda_map;
+static void __iomem *rtl_cmd_addr;
+static u8 rtl_cmd_type;
+static u8 rtl_cmd_width;
+
+static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)
+{
+ if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
+ return ioremap(addr, len);
+ return ioport_map(addr, len);
+}
+
+static void rtl_port_unmap(void __iomem *addr)
+{
+ if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
+ iounmap(addr);
+ else
+ ioport_unmap(addr);
+}
+
+static int ibm_rtl_write(u8 value)
+{
+ int ret = 0, count = 0;
+ static u32 cmd_port_val;
+
+ RTL_DEBUG("%s(%d)\n", __FUNCTION__, value);
+
+ value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM;
+
+ mutex_lock(&rtl_lock);
+
+ if (ioread8(&rtl_table->rt_status) != value) {
+ iowrite8(value, &rtl_table->command);
+
+ switch (rtl_cmd_width) {
+ case 8:
+ cmd_port_val = ioread8(&rtl_table->cmd_port_value);
+ RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
+ iowrite8((u8)cmd_port_val, rtl_cmd_addr);
+ break;
+ case 16:
+ cmd_port_val = ioread16(&rtl_table->cmd_port_value);
+ RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
+ iowrite16((u16)cmd_port_val, rtl_cmd_addr);
+ break;
+ case 32:
+ cmd_port_val = ioread32(&rtl_table->cmd_port_value);
+ RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
+ iowrite32(cmd_port_val, rtl_cmd_addr);
+ break;
+ }
+
+ while (ioread8(&rtl_table->command)) {
+ msleep(10);
+ if (count++ > 500) {
+ pr_err("ibm-rtl: Hardware not responding to "
+ "mode switch request\n");
+ ret = -EIO;
+ break;
+ }
+
+ }
+
+ if (ioread8(&rtl_table->command_status)) {
+ RTL_DEBUG("command_status reports failed command\n");
+ ret = -EIO;
+ }
+ }
+
+ mutex_unlock(&rtl_lock);
+ return ret;
+}
+
+static ssize_t rtl_show_version(struct sysdev_class * dev,
+ struct sysdev_class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version));
+}
+
+static ssize_t rtl_show_state(struct sysdev_class *dev,
+ struct sysdev_class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status));
+}
+
+static ssize_t rtl_set_state(struct sysdev_class *dev,
+ struct sysdev_class_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ ssize_t ret;
+
+ if (count < 1 || count > 2)
+ return -EINVAL;
+
+ switch (buf[0]) {
+ case '0':
+ ret = ibm_rtl_write(0);
+ break;
+ case '1':
+ ret = ibm_rtl_write(1);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret >= 0)
+ ret = count;
+
+ return ret;
+}
+
+static struct sysdev_class class_rtl = {
+ .name = "ibm_rtl",
+};
+
+static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL);
+static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state);
+
+static struct sysdev_class_attribute *rtl_attributes[] = {
+ &attr_version,
+ &attr_state,
+ NULL
+};
+
+
+static int rtl_setup_sysfs(void) {
+ int ret, i;
+ ret = sysdev_class_register(&class_rtl);
+
+ if (!ret) {
+ for (i = 0; rtl_attributes[i]; i ++)
+ sysdev_class_create_file(&class_rtl, rtl_attributes[i]);
+ }
+ return ret;
+}
+
+static void rtl_teardown_sysfs(void) {
+ int i;
+ for (i = 0; rtl_attributes[i]; i ++)
+ sysdev_class_remove_file(&class_rtl, rtl_attributes[i]);
+ sysdev_class_unregister(&class_rtl);
+}
+
+static int dmi_check_cb(const struct dmi_system_id *id)
+{
+ RTL_DEBUG("found IBM server '%s'\n", id->ident);
+ return 0;
+}
+
+#define ibm_dmi_entry(NAME, TYPE) \
+{ \
+ .ident = NAME, \
+ .matches = { \
+ DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \
+ DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \
+ }, \
+ .callback = dmi_check_cb \
+}
+
+static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = {
+ ibm_dmi_entry("BladeCenter LS21", "7971"),
+ ibm_dmi_entry("BladeCenter LS22", "7901"),
+ ibm_dmi_entry("BladeCenter HS21 XM", "7995"),
+ ibm_dmi_entry("BladeCenter HS22", "7870"),
+ ibm_dmi_entry("BladeCenter HS22V", "7871"),
+ ibm_dmi_entry("System x3550 M2", "7946"),
+ ibm_dmi_entry("System x3650 M2", "7947"),
+ ibm_dmi_entry("System x3550 M3", "7944"),
+ ibm_dmi_entry("System x3650 M3", "7945"),
+ { }
+};
+
+static int __init ibm_rtl_init(void) {
+ unsigned long ebda_addr, ebda_size;
+ unsigned int ebda_kb;
+ int ret = -ENODEV, i;
+
+ if (force)
+ pr_warning("ibm-rtl: module loaded by force\n");
+ /* first ensure that we are running on IBM HW */
+ else if (!dmi_check_system(ibm_rtl_dmi_table))
+ return -ENODEV;
+
+ /* Get the address for the Extended BIOS Data Area */
+ ebda_addr = get_bios_ebda();
+ if (!ebda_addr) {
+ RTL_DEBUG("no BIOS EBDA found\n");
+ return -ENODEV;
+ }
+
+ ebda_map = ioremap(ebda_addr, 4);
+ if (!ebda_map)
+ return -ENOMEM;
+
+ /* First word in the EDBA is the Size in KB */
+ ebda_kb = ioread16(ebda_map);
+ RTL_DEBUG("EBDA is %d kB\n", ebda_kb);
+
+ if (ebda_kb == 0)
+ goto out;
+
+ iounmap(ebda_map);
+ ebda_size = ebda_kb*1024;
+
+ /* Remap the whole table */
+ ebda_map = ioremap(ebda_addr, ebda_size);
+ if (!ebda_map)
+ return -ENOMEM;
+
+ /* search for the _RTL_ signature at the start of the table */
+ for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) {
+ struct ibm_rtl_table __iomem * tmp;
+ tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i);
+ if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) {
+ phys_addr_t addr;
+ unsigned int plen;
+ RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp);
+ rtl_table = tmp;
+ /* The address, value, width and offset are platform
+ * dependent and found in the ibm_rtl_table */
+ rtl_cmd_width = ioread8(&rtl_table->cmd_granularity);
+ rtl_cmd_type = ioread8(&rtl_table->cmd_address_type);
+ RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n",
+ rtl_cmd_width, rtl_cmd_type);
+ addr = ioread32(&rtl_table->cmd_port_address);
+ RTL_DEBUG("addr = %#llx\n", addr);
+ plen = rtl_cmd_width/sizeof(char);
+ rtl_cmd_addr = rtl_port_map(addr, plen);
+ RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr);
+ if (!rtl_cmd_addr) {
+ ret = -ENOMEM;
+ break;
+ }
+ ret = rtl_setup_sysfs();
+ break;
+ }
+ }
+
+out:
+ if (ret) {
+ iounmap(ebda_map);
+ rtl_port_unmap(rtl_cmd_addr);
+ }
+
+ return ret;
+}
+
+static void __exit ibm_rtl_exit(void)
+{
+ if (rtl_table) {
+ RTL_DEBUG("cleaning up");
+ /* do not leave the machine in SMI-free mode */
+ ibm_rtl_write(0);
+ /* unmap, unlink and remove all traces */
+ rtl_teardown_sysfs();
+ iounmap(ebda_map);
+ rtl_port_unmap(rtl_cmd_addr);
+ }
+}
+
+module_init(ibm_rtl_init);
+module_exit(ibm_rtl_exit);
diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad-laptop.c
index 798496353e8c..5ff12205aa6b 100644
--- a/drivers/platform/x86/ideapad_acpi.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -35,112 +35,162 @@
#define IDEAPAD_DEV_KILLSW 4
struct ideapad_private {
+ acpi_handle handle;
struct rfkill *rfk[5];
-};
+} *ideapad_priv;
static struct {
char *name;
+ int cfgbit;
+ int opcode;
int type;
} ideapad_rfk_data[] = {
- /* camera has no rfkill */
- { "ideapad_wlan", RFKILL_TYPE_WLAN },
- { "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH },
- { "ideapad_3g", RFKILL_TYPE_WWAN },
- { "ideapad_killsw", RFKILL_TYPE_WLAN }
+ { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES },
+ { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN },
+ { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH },
+ { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN },
+ { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN }
};
-static int ideapad_dev_exists(int device)
-{
- acpi_status status;
- union acpi_object in_param;
- struct acpi_object_list input = { 1, &in_param };
- struct acpi_buffer output;
- union acpi_object out_obj;
+static bool no_bt_rfkill;
+module_param(no_bt_rfkill, bool, 0444);
+MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
- output.length = sizeof(out_obj);
- output.pointer = &out_obj;
+/*
+ * ACPI Helpers
+ */
+#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
- in_param.type = ACPI_TYPE_INTEGER;
- in_param.integer.value = device + 1;
+static int read_method_int(acpi_handle handle, const char *method, int *val)
+{
+ acpi_status status;
+ unsigned long long result;
- status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output);
+ status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
if (ACPI_FAILURE(status)) {
- printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status);
- return -ENODEV;
- }
- if (out_obj.type != ACPI_TYPE_INTEGER) {
- printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n");
- return -ENODEV;
+ *val = -1;
+ return -1;
+ } else {
+ *val = result;
+ return 0;
}
- return out_obj.integer.value;
}
-static int ideapad_dev_get_state(int device)
+static int method_vpcr(acpi_handle handle, int cmd, int *ret)
{
acpi_status status;
- union acpi_object in_param;
- struct acpi_object_list input = { 1, &in_param };
- struct acpi_buffer output;
- union acpi_object out_obj;
+ unsigned long long result;
+ struct acpi_object_list params;
+ union acpi_object in_obj;
- output.length = sizeof(out_obj);
- output.pointer = &out_obj;
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = cmd;
- in_param.type = ACPI_TYPE_INTEGER;
- in_param.integer.value = device + 1;
+ status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
- status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output);
if (ACPI_FAILURE(status)) {
- printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status);
- return -ENODEV;
- }
- if (out_obj.type != ACPI_TYPE_INTEGER) {
- printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n");
- return -ENODEV;
+ *ret = -1;
+ return -1;
+ } else {
+ *ret = result;
+ return 0;
}
- return out_obj.integer.value;
}
-static int ideapad_dev_set_state(int device, int state)
+static int method_vpcw(acpi_handle handle, int cmd, int data)
{
+ struct acpi_object_list params;
+ union acpi_object in_obj[2];
acpi_status status;
- union acpi_object in_params[2];
- struct acpi_object_list input = { 2, in_params };
- in_params[0].type = ACPI_TYPE_INTEGER;
- in_params[0].integer.value = device + 1;
- in_params[1].type = ACPI_TYPE_INTEGER;
- in_params[1].integer.value = state;
+ params.count = 2;
+ params.pointer = in_obj;
+ in_obj[0].type = ACPI_TYPE_INTEGER;
+ in_obj[0].integer.value = cmd;
+ in_obj[1].type = ACPI_TYPE_INTEGER;
+ in_obj[1].integer.value = data;
- status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL);
- if (ACPI_FAILURE(status)) {
- printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status);
- return -ENODEV;
- }
+ status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
+ if (status != AE_OK)
+ return -1;
return 0;
}
+
+static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
+{
+ int val;
+ unsigned long int end_jiffies;
+
+ if (method_vpcw(handle, 1, cmd))
+ return -1;
+
+ for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
+ time_before(jiffies, end_jiffies);) {
+ schedule();
+ if (method_vpcr(handle, 1, &val))
+ return -1;
+ if (val == 0) {
+ if (method_vpcr(handle, 0, &val))
+ return -1;
+ *data = val;
+ return 0;
+ }
+ }
+ pr_err("timeout in read_ec_cmd\n");
+ return -1;
+}
+
+static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
+{
+ int val;
+ unsigned long int end_jiffies;
+
+ if (method_vpcw(handle, 0, data))
+ return -1;
+ if (method_vpcw(handle, 1, cmd))
+ return -1;
+
+ for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
+ time_before(jiffies, end_jiffies);) {
+ schedule();
+ if (method_vpcr(handle, 1, &val))
+ return -1;
+ if (val == 0)
+ return 0;
+ }
+ pr_err("timeout in write_ec_cmd\n");
+ return -1;
+}
+/* the above is ACPI helpers */
+
static ssize_t show_ideapad_cam(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA);
- if (state < 0)
- return state;
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ acpi_handle handle = priv->handle;
+ unsigned long result;
- return sprintf(buf, "%d\n", state);
+ if (read_ec_data(handle, 0x1D, &result))
+ return sprintf(buf, "-1\n");
+ return sprintf(buf, "%lu\n", result);
}
static ssize_t store_ideapad_cam(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ acpi_handle handle = priv->handle;
int ret, state;
if (!count)
return 0;
if (sscanf(buf, "%i", &state) != 1)
return -EINVAL;
- ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state);
+ ret = write_ec_cmd(handle, 0x1E, state);
if (ret < 0)
return ret;
return count;
@@ -154,7 +204,10 @@ static int ideapad_rfk_set(void *data, bool blocked)
if (device == IDEAPAD_DEV_KILLSW)
return -EINVAL;
- return ideapad_dev_set_state(device, !blocked);
+
+ return write_ec_cmd(ideapad_priv->handle,
+ ideapad_rfk_data[device].opcode,
+ !blocked);
}
static struct rfkill_ops ideapad_rfk_ops = {
@@ -164,32 +217,47 @@ static struct rfkill_ops ideapad_rfk_ops = {
static void ideapad_sync_rfk_state(struct acpi_device *adevice)
{
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
- int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW);
+ acpi_handle handle = priv->handle;
+ unsigned long hw_blocked;
int i;
- rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked);
- for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
- if (priv->rfk[i])
- rfkill_set_hw_state(priv->rfk[i], hw_blocked);
- if (hw_blocked)
+ if (read_ec_data(handle, 0x23, &hw_blocked))
return;
+ hw_blocked = !hw_blocked;
- for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
+ for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
if (priv->rfk[i])
- rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i));
+ rfkill_set_hw_state(priv->rfk[i], hw_blocked);
}
static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
{
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int ret;
+ unsigned long sw_blocked;
+
+ if (no_bt_rfkill &&
+ (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
+ /* Force to enable bluetooth when no_bt_rfkill=1 */
+ write_ec_cmd(ideapad_priv->handle,
+ ideapad_rfk_data[dev].opcode, 1);
+ return 0;
+ }
- priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev,
- ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops,
+ priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
+ ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
(void *)(long)dev);
if (!priv->rfk[dev])
return -ENOMEM;
+ if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1,
+ &sw_blocked)) {
+ rfkill_init_sw_state(priv->rfk[dev], 0);
+ } else {
+ sw_blocked = !sw_blocked;
+ rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
+ }
+
ret = rfkill_register(priv->rfk[dev]);
if (ret) {
rfkill_destroy(priv->rfk[dev]);
@@ -217,14 +285,18 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
static int ideapad_acpi_add(struct acpi_device *adevice)
{
- int i;
+ int i, cfg;
int devs_present[5];
struct ideapad_private *priv;
+ if (read_method_int(adevice->handle, "_CFG", &cfg))
+ return -ENODEV;
+
for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
- devs_present[i] = ideapad_dev_exists(i);
- if (devs_present[i] < 0)
- return devs_present[i];
+ if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
+ devs_present[i] = 1;
+ else
+ devs_present[i] = 0;
}
/* The hardware switch is always present */
@@ -242,7 +314,9 @@ static int ideapad_acpi_add(struct acpi_device *adevice)
}
}
+ priv->handle = adevice->handle;
dev_set_drvdata(&adevice->dev, priv);
+ ideapad_priv = priv;
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
if (!devs_present[i])
continue;
@@ -270,7 +344,21 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
{
- ideapad_sync_rfk_state(adevice);
+ acpi_handle handle = adevice->handle;
+ unsigned long vpc1, vpc2, vpc_bit;
+
+ if (read_ec_data(handle, 0x10, &vpc1))
+ return;
+ if (read_ec_data(handle, 0x1A, &vpc2))
+ return;
+
+ vpc1 = (vpc2 << 8) | vpc1;
+ for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
+ if (test_bit(vpc_bit, &vpc1)) {
+ if (vpc_bit == 9)
+ ideapad_sync_rfk_state(adevice);
+ }
+ }
}
static struct acpi_driver ideapad_acpi_driver = {
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c
index 5cdcff653918..f540ff96c53f 100644
--- a/drivers/platform/x86/intel_pmic_gpio.c
+++ b/drivers/platform/x86/intel_pmic_gpio.c
@@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip,
if (offset < 8)/* it is GPIO */
rc = intel_scu_ipc_update_register(GPIO0 + offset,
- GPIO_DRV | GPIO_DOU | GPIO_DIR,
- GPIO_DRV | (value ? GPIO_DOU : 0));
+ GPIO_DRV | (value ? GPIO_DOU : 0),
+ GPIO_DRV | GPIO_DOU | GPIO_DIR);
else if (offset < 16)/* it is GPOSW */
rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
- GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
- GPOSW_DRV | (value ? GPOSW_DOU : 0));
+ GPOSW_DRV | (value ? GPOSW_DOU : 0),
+ GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
else if (offset > 15 && offset < 24)/* it is GPO */
rc = intel_scu_ipc_update_register(GPO,
- 1 << (offset - 16),
- value ? 1 << (offset - 16) : 0);
+ value ? 1 << (offset - 16) : 0,
+ 1 << (offset - 16));
else {
printk(KERN_ERR
"%s: invalid PMIC GPIO pin %d!\n", __func__, offset);
@@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
if (offset < 8)/* it is GPIO */
intel_scu_ipc_update_register(GPIO0 + offset,
- GPIO_DRV | GPIO_DOU,
- GPIO_DRV | (value ? GPIO_DOU : 0));
+ GPIO_DRV | (value ? GPIO_DOU : 0),
+ GPIO_DRV | GPIO_DOU);
else if (offset < 16)/* it is GPOSW */
intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
- GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
- GPOSW_DRV | (value ? GPOSW_DOU : 0));
+ GPOSW_DRV | (value ? GPOSW_DOU : 0),
+ GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
else if (offset > 15 && offset < 24) /* it is GPO */
intel_scu_ipc_update_register(GPO,
- 1 << (offset - 16),
- value ? 1 << (offset - 16) : 0);
+ value ? 1 << (offset - 16) : 0,
+ 1 << (offset - 16));
}
static int pmic_irq_type(unsigned irq, unsigned type)
@@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type)
u32 gpio = irq - pg->irq_base;
unsigned long flags;
- if (gpio > pg->chip.ngpio)
+ if (gpio >= pg->chip.ngpio)
return -EINVAL;
spin_lock_irqsave(&pg->irqtypes.lock, flags);
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 6abe18e638e9..41a9e34899ac 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -23,6 +23,7 @@
#include <linux/pm.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/sfi.h>
#include <asm/mrst.h>
#include <asm/intel_scu_ipc.h>
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index ec01c3d8fc5a..cc1e0ba104d7 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -128,6 +128,7 @@
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#ifndef ACPI_HOTKEY_COMPONENT
@@ -200,30 +201,29 @@ static struct acpi_driver acpi_pcc_driver = {
},
};
-#define KEYMAP_SIZE 11
-static const unsigned int initial_keymap[KEYMAP_SIZE] = {
- /* 0 */ KEY_RESERVED,
- /* 1 */ KEY_BRIGHTNESSDOWN,
- /* 2 */ KEY_BRIGHTNESSUP,
- /* 3 */ KEY_DISPLAYTOGGLE,
- /* 4 */ KEY_MUTE,
- /* 5 */ KEY_VOLUMEDOWN,
- /* 6 */ KEY_VOLUMEUP,
- /* 7 */ KEY_SLEEP,
- /* 8 */ KEY_PROG1, /* Change CPU boost */
- /* 9 */ KEY_BATTERY,
- /* 10 */ KEY_SUSPEND,
+static const struct key_entry panasonic_keymap[] = {
+ { KE_KEY, 0, { KEY_RESERVED } },
+ { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, 2, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
+ { KE_KEY, 4, { KEY_MUTE } },
+ { KE_KEY, 5, { KEY_VOLUMEDOWN } },
+ { KE_KEY, 6, { KEY_VOLUMEUP } },
+ { KE_KEY, 7, { KEY_SLEEP } },
+ { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
+ { KE_KEY, 9, { KEY_BATTERY } },
+ { KE_KEY, 10, { KEY_SUSPEND } },
+ { KE_END, 0 }
};
struct pcc_acpi {
acpi_handle handle;
unsigned long num_sifr;
int sticky_mode;
- u32 *sinf;
+ u32 *sinf;
struct acpi_device *device;
struct input_dev *input_dev;
struct backlight_device *backlight;
- unsigned int keymap[KEYMAP_SIZE];
};
struct pcc_keyinput {
@@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device)
}
}
-static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
+static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
{
acpi_status status;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
@@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
hkey = buffer.pointer;
if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
+ status = AE_ERROR;
goto end;
}
@@ -298,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
for (i = 0; i < hkey->package.count; i++) {
union acpi_object *element = &(hkey->package.elements[i]);
if (likely(element->type == ACPI_TYPE_INTEGER)) {
- sinf[i] = element->integer.value;
+ pcc->sinf[i] = element->integer.value;
} else
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid HKEY.SINF data\n"));
}
- sinf[hkey->package.count] = -1;
+ pcc->sinf[hkey->package.count] = -1;
end:
kfree(buffer.pointer);
@@ -321,7 +322,7 @@ static int bl_get(struct backlight_device *bd)
{
struct pcc_acpi *pcc = bl_get_data(bd);
- if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+ if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return pcc->sinf[SINF_AC_CUR_BRIGHT];
@@ -333,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd)
int bright = bd->props.brightness;
int rc;
- if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+ if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
@@ -367,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
- if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+ if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
@@ -379,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
- if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+ if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
@@ -391,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
- if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+ if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
@@ -403,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
- if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+ if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
@@ -446,56 +447,10 @@ static struct attribute_group pcc_attr_group = {
/* hotkey input device driver */
-static int pcc_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
-{
- struct pcc_acpi *pcc = input_get_drvdata(dev);
-
- if (scancode >= ARRAY_SIZE(pcc->keymap))
- return -EINVAL;
-
- *keycode = pcc->keymap[scancode];
-
- return 0;
-}
-
-static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
- if (pcc->keymap[i] == keycode)
- return i+1;
- }
-
- return 0;
-}
-
-static int pcc_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
-{
- struct pcc_acpi *pcc = input_get_drvdata(dev);
- int oldkeycode;
-
- if (scancode >= ARRAY_SIZE(pcc->keymap))
- return -EINVAL;
-
- oldkeycode = pcc->keymap[scancode];
- pcc->keymap[scancode] = keycode;
-
- set_bit(keycode, dev->keybit);
-
- if (!keymap_get_by_keycode(pcc, oldkeycode))
- clear_bit(oldkeycode, dev->keybit);
-
- return 0;
-}
-
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
{
struct input_dev *hotk_input_dev = pcc->input_dev;
int rc;
- int key_code, hkey_num;
unsigned long long result;
rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
@@ -508,25 +463,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
- hkey_num = result & 0xf;
-
- if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) {
+ if (!sparse_keymap_report_event(hotk_input_dev,
+ result & 0xf, result & 0x80, false))
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "hotkey number out of range: %d\n",
- hkey_num));
- return;
- }
-
- key_code = pcc->keymap[hkey_num];
-
- if (key_code != KEY_RESERVED) {
- int pushed = (result & 0x80) ? TRUE : FALSE;
-
- input_report_key(hotk_input_dev, key_code, pushed);
- input_sync(hotk_input_dev);
- }
-
- return;
+ "Unknown hotkey event: %d\n", result));
}
static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
@@ -545,40 +485,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
static int acpi_pcc_init_input(struct pcc_acpi *pcc)
{
- int i, rc;
+ struct input_dev *input_dev;
+ int error;
- pcc->input_dev = input_allocate_device();
- if (!pcc->input_dev) {
+ input_dev = input_allocate_device();
+ if (!input_dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't allocate input device for hotkey"));
return -ENOMEM;
}
- pcc->input_dev->evbit[0] = BIT(EV_KEY);
-
- pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
- pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
- pcc->input_dev->id.bustype = BUS_HOST;
- pcc->input_dev->id.vendor = 0x0001;
- pcc->input_dev->id.product = 0x0001;
- pcc->input_dev->id.version = 0x0100;
- pcc->input_dev->getkeycode = pcc_getkeycode;
- pcc->input_dev->setkeycode = pcc_setkeycode;
+ input_dev->name = ACPI_PCC_DRIVER_NAME;
+ input_dev->phys = ACPI_PCC_INPUT_PHYS;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
- /* load initial keymap */
- memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
+ error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
+ if (error) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Unable to setup input device keymap\n"));
+ goto err_free_dev;
+ }
- for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
- __set_bit(pcc->keymap[i], pcc->input_dev->keybit);
- __clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
+ error = input_register_device(input_dev);
+ if (error) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Unable to register input device\n"));
+ goto err_free_keymap;
+ }
- input_set_drvdata(pcc->input_dev, pcc);
+ pcc->input_dev = input_dev;
+ return 0;
- rc = input_register_device(pcc->input_dev);
- if (rc < 0)
- input_free_device(pcc->input_dev);
+ err_free_keymap:
+ sparse_keymap_free(input_dev);
+ err_free_dev:
+ input_free_device(input_dev);
+ return error;
+}
- return rc;
+static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
+{
+ sparse_keymap_free(pcc->input_dev);
+ input_unregister_device(pcc->input_dev);
+ /*
+ * No need to input_free_device() since core input API refcounts
+ * and free()s the device.
+ */
}
/* kernel module interface */
@@ -636,12 +591,13 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error installing keyinput handler\n"));
- goto out_hotkey;
+ goto out_sinf;
}
- if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
+ if (!acpi_pcc_retrieve_biosdata(pcc)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't retrieve BIOS data\n"));
+ result = -EIO;
goto out_input;
}
/* initialize backlight */
@@ -651,7 +607,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
&pcc_backlight_ops, &props);
if (IS_ERR(pcc->backlight)) {
result = PTR_ERR(pcc->backlight);
- goto out_sinf;
+ goto out_input;
}
/* read the initial brightness setting from the hardware */
@@ -669,12 +625,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
out_backlight:
backlight_device_unregister(pcc->backlight);
+out_input:
+ acpi_pcc_destroy_input(pcc);
out_sinf:
kfree(pcc->sinf);
-out_input:
- input_unregister_device(pcc->input_dev);
- /* no need to input_free_device() since core input API refcount and
- * free()s the device */
out_hotkey:
kfree(pcc);
@@ -709,9 +663,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
backlight_device_unregister(pcc->backlight);
- input_unregister_device(pcc->input_dev);
- /* no need to input_free_device() since core input API refcount and
- * free()s the device */
+ acpi_pcc_destroy_input(pcc);
kfree(pcc->sinf);
kfree(pcc);
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c
index ff4b476f1950..1d07d6d09f27 100644
--- a/drivers/platform/x86/topstar-laptop.c
+++ b/drivers/platform/x86/topstar-laptop.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#define ACPI_TOPSTAR_CLASS "topstar"
@@ -26,52 +27,37 @@ struct topstar_hkey {
struct input_dev *inputdev;
};
-struct tps_key_entry {
- u8 code;
- u16 keycode;
-};
-
-static struct tps_key_entry topstar_keymap[] = {
- { 0x80, KEY_BRIGHTNESSUP },
- { 0x81, KEY_BRIGHTNESSDOWN },
- { 0x83, KEY_VOLUMEUP },
- { 0x84, KEY_VOLUMEDOWN },
- { 0x85, KEY_MUTE },
- { 0x86, KEY_SWITCHVIDEOMODE },
- { 0x87, KEY_F13 }, /* touchpad enable/disable key */
- { 0x88, KEY_WLAN },
- { 0x8a, KEY_WWW },
- { 0x8b, KEY_MAIL },
- { 0x8c, KEY_MEDIA },
- { 0x96, KEY_F14 }, /* G key? */
- { }
-};
-
-static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code)
-{
- struct tps_key_entry *key;
-
- for (key = topstar_keymap; key->code; key++)
- if (code == key->code)
- return key;
+static const struct key_entry topstar_keymap[] = {
+ { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, 0x83, { KEY_VOLUMEUP } },
+ { KE_KEY, 0x84, { KEY_VOLUMEDOWN } },
+ { KE_KEY, 0x85, { KEY_MUTE } },
+ { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */
+ { KE_KEY, 0x88, { KEY_WLAN } },
+ { KE_KEY, 0x8a, { KEY_WWW } },
+ { KE_KEY, 0x8b, { KEY_MAIL } },
+ { KE_KEY, 0x8c, { KEY_MEDIA } },
- return NULL;
-}
-
-static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code)
-{
- struct tps_key_entry *key;
+ /* Known non hotkey events don't handled or that we don't care yet */
+ { KE_IGNORE, 0x8e, },
+ { KE_IGNORE, 0x8f, },
+ { KE_IGNORE, 0x90, },
- for (key = topstar_keymap; key->code; key++)
- if (code == key->keycode)
- return key;
+ /*
+ * 'G key' generate two event codes, convert to only
+ * one event/key code for now, consider replacing by
+ * a switch (3G switch - SW_3G?)
+ */
+ { KE_KEY, 0x96, { KEY_F14 } },
+ { KE_KEY, 0x97, { KEY_F14 } },
- return NULL;
-}
+ { KE_END, 0 }
+};
static void acpi_topstar_notify(struct acpi_device *device, u32 event)
{
- struct tps_key_entry *key;
static bool dup_evnt[2];
bool *dup;
struct topstar_hkey *hkey = acpi_driver_data(device);
@@ -86,27 +72,8 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event)
*dup = true;
}
- /*
- * 'G key' generate two event codes, convert to only
- * one event/key code for now (3G switch?)
- */
- if (event == 0x97)
- event = 0x96;
-
- key = tps_get_key_by_scancode(event);
- if (key) {
- input_report_key(hkey->inputdev, key->keycode, 1);
- input_sync(hkey->inputdev);
- input_report_key(hkey->inputdev, key->keycode, 0);
- input_sync(hkey->inputdev);
- return;
- }
-
- /* Known non hotkey events don't handled or that we don't care yet */
- if (event == 0x8e || event == 0x8f || event == 0x90)
- return;
-
- pr_info("unknown event = 0x%02x\n", event);
+ if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
+ pr_info("unknown event = 0x%02x\n", event);
}
static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
@@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
return 0;
}
-static int topstar_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
-{
- struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
-
- if (!key)
- return -EINVAL;
-
- *keycode = key->keycode;
- return 0;
-}
-
-static int topstar_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
-{
- struct tps_key_entry *key;
- int old_keycode;
-
- key = tps_get_key_by_scancode(scancode);
-
- if (!key)
- return -EINVAL;
-
- old_keycode = key->keycode;
- key->keycode = keycode;
- set_bit(keycode, dev->keybit);
- if (!tps_get_key_by_keycode(old_keycode))
- clear_bit(old_keycode, dev->keybit);
- return 0;
-}
-
static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
{
- struct tps_key_entry *key;
+ struct input_dev *input;
+ int error;
- hkey->inputdev = input_allocate_device();
- if (!hkey->inputdev) {
+ input = input_allocate_device();
+ if (!input) {
pr_err("Unable to allocate input device\n");
- return -ENODEV;
+ return -ENOMEM;
}
- hkey->inputdev->name = "Topstar Laptop extra buttons";
- hkey->inputdev->phys = "topstar/input0";
- hkey->inputdev->id.bustype = BUS_HOST;
- hkey->inputdev->getkeycode = topstar_getkeycode;
- hkey->inputdev->setkeycode = topstar_setkeycode;
- for (key = topstar_keymap; key->code; key++) {
- set_bit(EV_KEY, hkey->inputdev->evbit);
- set_bit(key->keycode, hkey->inputdev->keybit);
+
+ input->name = "Topstar Laptop extra buttons";
+ input->phys = "topstar/input0";
+ input->id.bustype = BUS_HOST;
+
+ error = sparse_keymap_setup(input, topstar_keymap, NULL);
+ if (error) {
+ pr_err("Unable to setup input device keymap\n");
+ goto err_free_dev;
}
- if (input_register_device(hkey->inputdev)) {
+
+ error = input_register_device(input);
+ if (error) {
pr_err("Unable to register input device\n");
- input_free_device(hkey->inputdev);
- return -ENODEV;
+ goto err_free_keymap;
}
+ hkey->inputdev = input;
return 0;
+
+ err_free_keymap:
+ sparse_keymap_free(input);
+ err_free_dev:
+ input_free_device(input);
+ return error;
}
static int acpi_topstar_add(struct acpi_device *device)
@@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type)
acpi_topstar_fncx_switch(device, false);
+ sparse_keymap_free(tps_hkey->inputdev);
input_unregister_device(tps_hkey->inputdev);
kfree(tps_hkey);
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 7d67a45bb2b0..06f304f46e02 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -48,6 +48,7 @@
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
#include <linux/slab.h>
@@ -121,36 +122,28 @@ static const struct acpi_device_id toshiba_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
-struct key_entry {
- char type;
- u16 code;
- u16 keycode;
-};
-
-enum {KE_KEY, KE_END};
-
-static struct key_entry toshiba_acpi_keymap[] = {
- {KE_KEY, 0x101, KEY_MUTE},
- {KE_KEY, 0x102, KEY_ZOOMOUT},
- {KE_KEY, 0x103, KEY_ZOOMIN},
- {KE_KEY, 0x13b, KEY_COFFEE},
- {KE_KEY, 0x13c, KEY_BATTERY},
- {KE_KEY, 0x13d, KEY_SLEEP},
- {KE_KEY, 0x13e, KEY_SUSPEND},
- {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
- {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
- {KE_KEY, 0x141, KEY_BRIGHTNESSUP},
- {KE_KEY, 0x142, KEY_WLAN},
- {KE_KEY, 0x143, KEY_PROG1},
- {KE_KEY, 0xb05, KEY_PROG2},
- {KE_KEY, 0xb06, KEY_WWW},
- {KE_KEY, 0xb07, KEY_MAIL},
- {KE_KEY, 0xb30, KEY_STOP},
- {KE_KEY, 0xb31, KEY_PREVIOUSSONG},
- {KE_KEY, 0xb32, KEY_NEXTSONG},
- {KE_KEY, 0xb33, KEY_PLAYPAUSE},
- {KE_KEY, 0xb5a, KEY_MEDIA},
- {KE_END, 0, 0},
+static const struct key_entry toshiba_acpi_keymap[] __initconst = {
+ { KE_KEY, 0x101, { KEY_MUTE } },
+ { KE_KEY, 0x102, { KEY_ZOOMOUT } },
+ { KE_KEY, 0x103, { KEY_ZOOMIN } },
+ { KE_KEY, 0x13b, { KEY_COFFEE } },
+ { KE_KEY, 0x13c, { KEY_BATTERY } },
+ { KE_KEY, 0x13d, { KEY_SLEEP } },
+ { KE_KEY, 0x13e, { KEY_SUSPEND } },
+ { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 0x142, { KEY_WLAN } },
+ { KE_KEY, 0x143, { KEY_PROG1 } },
+ { KE_KEY, 0xb05, { KEY_PROG2 } },
+ { KE_KEY, 0xb06, { KEY_WWW } },
+ { KE_KEY, 0xb07, { KEY_MAIL } },
+ { KE_KEY, 0xb30, { KEY_STOP } },
+ { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
+ { KE_KEY, 0xb32, { KEY_NEXTSONG } },
+ { KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
+ { KE_KEY, 0xb5a, { KEY_MEDIA } },
+ { KE_END, 0 },
};
/* utility
@@ -852,64 +845,9 @@ static struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
-static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
-{
- struct key_entry *key;
-
- for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
- if (code == key->code)
- return key;
-
- return NULL;
-}
-
-static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
-{
- struct key_entry *key;
-
- for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
- if (code == key->keycode && key->type == KE_KEY)
- return key;
-
- return NULL;
-}
-
-static int toshiba_acpi_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
-{
- struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
-
- if (key && key->type == KE_KEY) {
- *keycode = key->keycode;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int toshiba_acpi_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
-{
- struct key_entry *key;
- unsigned int old_keycode;
-
- key = toshiba_acpi_get_entry_by_scancode(scancode);
- if (key && key->type == KE_KEY) {
- old_keycode = key->keycode;
- key->keycode = keycode;
- set_bit(keycode, dev->keybit);
- if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
- clear_bit(old_keycode, dev->keybit);
- return 0;
- }
-
- return -EINVAL;
-}
-
static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
{
u32 hci_result, value;
- struct key_entry *key;
if (event != 0x80)
return;
@@ -922,19 +860,11 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
if (value & 0x80)
continue;
- key = toshiba_acpi_get_entry_by_scancode
- (value);
- if (!key) {
+ if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
+ value, 1, true)) {
printk(MY_INFO "Unknown key %x\n",
value);
- continue;
}
- input_report_key(toshiba_acpi.hotkey_dev,
- key->keycode, 1);
- input_sync(toshiba_acpi.hotkey_dev);
- input_report_key(toshiba_acpi.hotkey_dev,
- key->keycode, 0);
- input_sync(toshiba_acpi.hotkey_dev);
} else if (hci_result == HCI_NOT_SUPPORTED) {
/* This is a workaround for an unresolved issue on
* some machines where system events sporadically
@@ -945,34 +875,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
} while (hci_result != HCI_EMPTY);
}
-static int toshiba_acpi_setup_keyboard(char *device)
+static int __init toshiba_acpi_setup_keyboard(char *device)
{
acpi_status status;
- acpi_handle handle;
- int result;
- const struct key_entry *key;
+ int error;
- status = acpi_get_handle(NULL, device, &handle);
+ status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to get notification device\n");
return -ENODEV;
}
- toshiba_acpi.handle = handle;
-
- status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
- if (ACPI_FAILURE(status)) {
- printk(MY_INFO "Unable to enable hotkeys\n");
- return -ENODEV;
- }
-
- status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
- toshiba_acpi_notify, NULL);
- if (ACPI_FAILURE(status)) {
- printk(MY_INFO "Unable to install hotkey notification\n");
- return -ENODEV;
- }
-
toshiba_acpi.hotkey_dev = input_allocate_device();
if (!toshiba_acpi.hotkey_dev) {
printk(MY_INFO "Unable to register input device\n");
@@ -982,27 +895,54 @@ static int toshiba_acpi_setup_keyboard(char *device)
toshiba_acpi.hotkey_dev->name = "Toshiba input device";
toshiba_acpi.hotkey_dev->phys = device;
toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
- toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
- toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
- for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
- set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
- set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
+ error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
+ toshiba_acpi_keymap, NULL);
+ if (error)
+ goto err_free_dev;
+
+ status = acpi_install_notify_handler(toshiba_acpi.handle,
+ ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(MY_INFO "Unable to install hotkey notification\n");
+ error = -ENODEV;
+ goto err_free_keymap;
+ }
+
+ status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ printk(MY_INFO "Unable to enable hotkeys\n");
+ error = -ENODEV;
+ goto err_remove_notify;
}
- result = input_register_device(toshiba_acpi.hotkey_dev);
- if (result) {
+ error = input_register_device(toshiba_acpi.hotkey_dev);
+ if (error) {
printk(MY_INFO "Unable to register input device\n");
- return result;
+ goto err_remove_notify;
}
return 0;
+
+ err_remove_notify:
+ acpi_remove_notify_handler(toshiba_acpi.handle,
+ ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
+ err_free_keymap:
+ sparse_keymap_free(toshiba_acpi.hotkey_dev);
+ err_free_dev:
+ input_free_device(toshiba_acpi.hotkey_dev);
+ toshiba_acpi.hotkey_dev = NULL;
+ return error;
}
static void toshiba_acpi_exit(void)
{
- if (toshiba_acpi.hotkey_dev)
+ if (toshiba_acpi.hotkey_dev) {
+ acpi_remove_notify_handler(toshiba_acpi.handle,
+ ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
+ sparse_keymap_free(toshiba_acpi.hotkey_dev);
input_unregister_device(toshiba_acpi.hotkey_dev);
+ }
if (toshiba_acpi.bt_rfk) {
rfkill_unregister(toshiba_acpi.bt_rfk);
@@ -1017,9 +957,6 @@ static void toshiba_acpi_exit(void)
if (toshiba_proc_dir)
remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
- acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
- toshiba_acpi_notify);
-
if (toshiba_acpi.illumination_installed)
led_classdev_unregister(&toshiba_led);
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index b2978a04317f..104b77c87ef5 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -27,6 +27,8 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -44,9 +46,8 @@ MODULE_LICENSE("GPL");
#define ACPI_WMI_CLASS "wmi"
-#define PREFIX "ACPI: WMI: "
-
static DEFINE_MUTEX(wmi_data_lock);
+static LIST_HEAD(wmi_block_list);
struct guid_block {
char guid[16];
@@ -67,10 +68,9 @@ struct wmi_block {
acpi_handle handle;
wmi_notify_handler handler;
void *handler_data;
- struct device *dev;
+ struct device dev;
};
-static struct wmi_block wmi_blocks;
/*
* If the GUID data block is marked as expensive, we must enable and
@@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = {
.add = acpi_wmi_add,
.remove = acpi_wmi_remove,
.notify = acpi_wmi_notify,
- },
+ },
};
/*
@@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = {
*/
static int wmi_parse_hexbyte(const u8 *src)
{
- unsigned int x; /* For correct wrapping */
int h;
+ int value;
/* high part */
- x = src[0];
- if (x - '0' <= '9' - '0') {
- h = x - '0';
- } else if (x - 'a' <= 'f' - 'a') {
- h = x - 'a' + 10;
- } else if (x - 'A' <= 'F' - 'A') {
- h = x - 'A' + 10;
- } else {
+ h = value = hex_to_bin(src[0]);
+ if (value < 0)
return -1;
- }
- h <<= 4;
/* low part */
- x = src[1];
- if (x - '0' <= '9' - '0')
- return h | (x - '0');
- if (x - 'a' <= 'f' - 'a')
- return h | (x - 'a' + 10);
- if (x - 'A' <= 'F' - 'A')
- return h | (x - 'A' + 10);
+ value = hex_to_bin(src[1]);
+ if (value >= 0)
+ return (h << 4) | value;
return -1;
}
@@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out)
for (i = 10; i <= 15; i++)
out += sprintf(out, "%02X", in[i] & 0xFF);
- out = '\0';
+ *out = '\0';
return 0;
}
@@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
wmi_parse_guid(guid_string, tmp);
wmi_swap_bytes(tmp, guid_input);
- list_for_each(p, &wmi_blocks.list) {
+ list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock;
@@ -487,30 +475,29 @@ const struct acpi_buffer *in)
}
EXPORT_SYMBOL_GPL(wmi_set_block);
-static void wmi_dump_wdg(struct guid_block *g)
+static void wmi_dump_wdg(const struct guid_block *g)
{
char guid_string[37];
wmi_gtoa(g->guid, guid_string);
- printk(KERN_INFO PREFIX "%s:\n", guid_string);
- printk(KERN_INFO PREFIX "\tobject_id: %c%c\n",
- g->object_id[0], g->object_id[1]);
- printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id);
- printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved);
- printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count);
- printk(KERN_INFO PREFIX "\tflags: %#x", g->flags);
+
+ pr_info("%s:\n", guid_string);
+ pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
+ pr_info("\tnotify_id: %02X\n", g->notify_id);
+ pr_info("\treserved: %02X\n", g->reserved);
+ pr_info("\tinstance_count: %d\n", g->instance_count);
+ pr_info("\tflags: %#x ", g->flags);
if (g->flags) {
- printk(" ");
if (g->flags & ACPI_WMI_EXPENSIVE)
- printk("ACPI_WMI_EXPENSIVE ");
+ pr_cont("ACPI_WMI_EXPENSIVE ");
if (g->flags & ACPI_WMI_METHOD)
- printk("ACPI_WMI_METHOD ");
+ pr_cont("ACPI_WMI_METHOD ");
if (g->flags & ACPI_WMI_STRING)
- printk("ACPI_WMI_STRING ");
+ pr_cont("ACPI_WMI_STRING ");
if (g->flags & ACPI_WMI_EVENT)
- printk("ACPI_WMI_EVENT ");
+ pr_cont("ACPI_WMI_EVENT ");
}
- printk("\n");
+ pr_cont("\n");
}
@@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context)
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
- printk(KERN_INFO "wmi: bad event status 0x%x\n", status);
+ pr_info("bad event status 0x%x\n", status);
return;
}
@@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context)
if (!obj)
return;
- printk(KERN_INFO PREFIX "DEBUG Event ");
+ pr_info("DEBUG Event ");
switch(obj->type) {
case ACPI_TYPE_BUFFER:
- printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
+ pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
break;
case ACPI_TYPE_STRING:
- printk("STRING_TYPE - %s\n", obj->string.pointer);
+ pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
break;
case ACPI_TYPE_INTEGER:
- printk("INTEGER_TYPE - %llu\n", obj->integer.value);
+ pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
break;
case ACPI_TYPE_PACKAGE:
- printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
+ pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
break;
default:
- printk("object type 0x%X\n", obj->type);
+ pr_cont("object type 0x%X\n", obj->type);
}
kfree(obj);
}
@@ -633,7 +620,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = event;
- list_for_each(p, &wmi_blocks.list) {
+ list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
gblock = &wblock->gblock;
@@ -662,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid);
/*
* sysfs interface
*/
-static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
char guid_string[37];
@@ -676,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "wmi:%s\n", guid_string);
}
-static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
+
+static struct device_attribute wmi_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL
+};
static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
@@ -702,108 +693,71 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
static void wmi_dev_free(struct device *dev)
{
- kfree(dev);
+ struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
+
+ kfree(wmi_block);
}
static struct class wmi_class = {
.name = "wmi",
.dev_release = wmi_dev_free,
.dev_uevent = wmi_dev_uevent,
+ .dev_attrs = wmi_dev_attrs,
};
-static int wmi_create_devs(void)
+static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
+ acpi_handle handle)
{
- int result;
- char guid_string[37];
- struct guid_block *gblock;
struct wmi_block *wblock;
- struct list_head *p;
- struct device *guid_dev;
-
- /* Create devices for all the GUIDs */
- list_for_each(p, &wmi_blocks.list) {
- wblock = list_entry(p, struct wmi_block, list);
-
- guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (!guid_dev)
- return -ENOMEM;
-
- wblock->dev = guid_dev;
-
- guid_dev->class = &wmi_class;
- dev_set_drvdata(guid_dev, wblock);
-
- gblock = &wblock->gblock;
-
- wmi_gtoa(gblock->guid, guid_string);
- dev_set_name(guid_dev, guid_string);
-
- result = device_register(guid_dev);
- if (result)
- return result;
+ int error;
+ char guid_string[37];
- result = device_create_file(guid_dev, &dev_attr_modalias);
- if (result)
- return result;
+ wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
+ if (!wblock) {
+ error = -ENOMEM;
+ goto err_out;
}
- return 0;
-}
+ wblock->handle = handle;
+ wblock->gblock = *gblock;
-static void wmi_remove_devs(void)
-{
- struct guid_block *gblock;
- struct wmi_block *wblock;
- struct list_head *p;
- struct device *guid_dev;
+ wblock->dev.class = &wmi_class;
- /* Delete devices for all the GUIDs */
- list_for_each(p, &wmi_blocks.list) {
- wblock = list_entry(p, struct wmi_block, list);
+ wmi_gtoa(gblock->guid, guid_string);
+ dev_set_name(&wblock->dev, guid_string);
- guid_dev = wblock->dev;
- gblock = &wblock->gblock;
+ dev_set_drvdata(&wblock->dev, wblock);
- device_remove_file(guid_dev, &dev_attr_modalias);
+ error = device_register(&wblock->dev);
+ if (error)
+ goto err_free;
- device_unregister(guid_dev);
- }
-}
+ list_add_tail(&wblock->list, &wmi_block_list);
+ return wblock;
-static void wmi_class_exit(void)
-{
- wmi_remove_devs();
- class_unregister(&wmi_class);
+err_free:
+ kfree(wblock);
+err_out:
+ return ERR_PTR(error);
}
-static int wmi_class_init(void)
+static void wmi_free_devices(void)
{
- int ret;
-
- ret = class_register(&wmi_class);
- if (ret)
- return ret;
+ struct wmi_block *wblock, *next;
- ret = wmi_create_devs();
- if (ret)
- wmi_class_exit();
-
- return ret;
+ /* Delete devices for all the GUIDs */
+ list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
+ device_unregister(&wblock->dev);
}
static bool guid_already_parsed(const char *guid_string)
{
- struct guid_block *gblock;
struct wmi_block *wblock;
- struct list_head *p;
- list_for_each(p, &wmi_blocks.list) {
- wblock = list_entry(p, struct wmi_block, list);
- gblock = &wblock->gblock;
-
- if (strncmp(gblock->guid, guid_string, 16) == 0)
+ list_for_each_entry(wblock, &wmi_block_list, list)
+ if (strncmp(wblock->gblock.guid, guid_string, 16) == 0)
return true;
- }
+
return false;
}
@@ -814,30 +768,29 @@ static acpi_status parse_wdg(acpi_handle handle)
{
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
- struct guid_block *gblock;
+ const struct guid_block *gblock;
struct wmi_block *wblock;
char guid_string[37];
acpi_status status;
+ int retval;
u32 i, total;
status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
-
if (ACPI_FAILURE(status))
- return status;
+ return -ENXIO;
obj = (union acpi_object *) out.pointer;
+ if (!obj)
+ return -ENXIO;
- if (obj->type != ACPI_TYPE_BUFFER)
- return AE_ERROR;
-
- total = obj->buffer.length / sizeof(struct guid_block);
-
- gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
- if (!gblock) {
- status = AE_NO_MEMORY;
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ retval = -ENXIO;
goto out_free_pointer;
}
+ gblock = (const struct guid_block *)obj->buffer.pointer;
+ total = obj->buffer.length / sizeof(struct guid_block);
+
for (i = 0; i < total; i++) {
/*
Some WMI devices, like those for nVidia hooks, have a
@@ -848,34 +801,32 @@ static acpi_status parse_wdg(acpi_handle handle)
*/
if (guid_already_parsed(gblock[i].guid) == true) {
wmi_gtoa(gblock[i].guid, guid_string);
- printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
- guid_string);
+ pr_info("Skipping duplicate GUID %s\n", guid_string);
continue;
}
+
if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]);
- wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
- if (!wblock) {
- status = AE_NO_MEMORY;
- goto out_free_gblock;
+ wblock = wmi_create_device(&gblock[i], handle);
+ if (IS_ERR(wblock)) {
+ retval = PTR_ERR(wblock);
+ wmi_free_devices();
+ break;
}
- wblock->gblock = gblock[i];
- wblock->handle = handle;
if (debug_event) {
wblock->handler = wmi_notify_debug;
- status = wmi_method_enable(wblock, 1);
+ wmi_method_enable(wblock, 1);
}
- list_add_tail(&wblock->list, &wmi_blocks.list);
}
-out_free_gblock:
- kfree(gblock);
+ retval = 0;
+
out_free_pointer:
kfree(out.pointer);
- return status;
+ return retval;
}
/*
@@ -929,7 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
struct list_head *p;
char guid_string[37];
- list_for_each(p, &wmi_blocks.list) {
+ list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock;
@@ -939,8 +890,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
wblock->handler(event, wblock->handler_data);
if (debug_event) {
wmi_gtoa(wblock->gblock.guid, guid_string);
- printk(KERN_INFO PREFIX "DEBUG Event GUID:"
- " %s\n", guid_string);
+ pr_info("DEBUG Event GUID: %s\n", guid_string);
}
acpi_bus_generate_netlink_event(
@@ -955,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
{
acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
+ wmi_free_devices();
return 0;
}
@@ -962,68 +913,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
static int acpi_wmi_add(struct acpi_device *device)
{
acpi_status status;
- int result = 0;
+ int error;
status = acpi_install_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler,
NULL, NULL);
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
- status = parse_wdg(device->handle);
if (ACPI_FAILURE(status)) {
- printk(KERN_ERR PREFIX "Error installing EC region handler\n");
+ pr_err("Error installing EC region handler\n");
return -ENODEV;
}
- return result;
+ error = parse_wdg(device->handle);
+ if (error) {
+ acpi_remove_address_space_handler(device->handle,
+ ACPI_ADR_SPACE_EC,
+ &acpi_wmi_ec_space_handler);
+ pr_err("Failed to parse WDG method\n");
+ return error;
+ }
+
+ return 0;
}
static int __init acpi_wmi_init(void)
{
- int result;
-
- INIT_LIST_HEAD(&wmi_blocks.list);
+ int error;
if (acpi_disabled)
return -ENODEV;
- result = acpi_bus_register_driver(&acpi_wmi_driver);
+ error = class_register(&wmi_class);
+ if (error)
+ return error;
- if (result < 0) {
- printk(KERN_INFO PREFIX "Error loading mapper\n");
- return -ENODEV;
+ error = acpi_bus_register_driver(&acpi_wmi_driver);
+ if (error) {
+ pr_err("Error loading mapper\n");
+ class_unregister(&wmi_class);
+ return error;
}
- result = wmi_class_init();
- if (result) {
- acpi_bus_unregister_driver(&acpi_wmi_driver);
- return result;
- }
-
- printk(KERN_INFO PREFIX "Mapper loaded\n");
-
- return result;
+ pr_info("Mapper loaded\n");
+ return 0;
}
static void __exit acpi_wmi_exit(void)
{
- struct list_head *p, *tmp;
- struct wmi_block *wblock;
-
- wmi_class_exit();
-
acpi_bus_unregister_driver(&acpi_wmi_driver);
+ class_unregister(&wmi_class);
- list_for_each_safe(p, tmp, &wmi_blocks.list) {
- wblock = list_entry(p, struct wmi_block, list);
-
- list_del(p);
- kfree(wblock);
- }
-
- printk(KERN_INFO PREFIX "Mapper unloaded\n");
+ pr_info("Mapper unloaded\n");
}
subsys_initcall(acpi_wmi_init);
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c
new file mode 100644
index 000000000000..e549eeeda121
--- /dev/null
+++ b/drivers/platform/x86/xo1-rfkill.c
@@ -0,0 +1,85 @@
+/*
+ * Support for rfkill through the OLPC XO-1 laptop embedded controller
+ *
+ * Copyright (C) 2010 One Laptop per Child
+ *
+ * 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/rfkill.h>
+
+#include <asm/olpc.h>
+
+static int rfkill_set_block(void *data, bool blocked)
+{
+ unsigned char cmd;
+ if (blocked)
+ cmd = EC_WLAN_ENTER_RESET;
+ else
+ cmd = EC_WLAN_LEAVE_RESET;
+
+ return olpc_ec_cmd(cmd, NULL, 0, NULL, 0);
+}
+
+static const struct rfkill_ops rfkill_ops = {
+ .set_block = rfkill_set_block,
+};
+
+static int __devinit xo1_rfkill_probe(struct platform_device *pdev)
+{
+ struct rfkill *rfk;
+ int r;
+
+ rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN,
+ &rfkill_ops, NULL);
+ if (!rfk)
+ return -ENOMEM;
+
+ r = rfkill_register(rfk);
+ if (r) {
+ rfkill_destroy(rfk);
+ return r;
+ }
+
+ platform_set_drvdata(pdev, rfk);
+ return 0;
+}
+
+static int __devexit xo1_rfkill_remove(struct platform_device *pdev)
+{
+ struct rfkill *rfk = platform_get_drvdata(pdev);
+ rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ return 0;
+}
+
+static struct platform_driver xo1_rfkill_driver = {
+ .driver = {
+ .name = "xo1-rfkill",
+ .owner = THIS_MODULE,
+ },
+ .probe = xo1_rfkill_probe,
+ .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_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/base.h b/drivers/pnp/base.h
index 0bab84ebb15d..19bc73695475 100644
--- a/drivers/pnp/base.h
+++ b/drivers/pnp/base.h
@@ -12,11 +12,12 @@ void pnp_unregister_protocol(struct pnp_protocol *protocol);
#define PNP_EISA_ID_MASK 0x7fffffff
void pnp_eisa_id_to_string(u32 id, char *str);
-struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *, int id, char *pnpid);
+struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *, int id,
+ const char *pnpid);
struct pnp_card *pnp_alloc_card(struct pnp_protocol *, int id, char *pnpid);
int pnp_add_device(struct pnp_dev *dev);
-struct pnp_id *pnp_add_id(struct pnp_dev *dev, char *id);
+struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id);
int pnp_add_card(struct pnp_card *card);
void pnp_remove_card(struct pnp_card *card);
diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c
index 88b3cde52596..0f34d962fd3c 100644
--- a/drivers/pnp/core.c
+++ b/drivers/pnp/core.c
@@ -124,7 +124,8 @@ static void pnp_release_device(struct device *dmdev)
kfree(dev);
}
-struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, char *pnpid)
+struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id,
+ const char *pnpid)
{
struct pnp_dev *dev;
struct pnp_id *dev_id;
@@ -194,8 +195,9 @@ int pnp_add_device(struct pnp_dev *dev)
for (id = dev->id; id; id = id->next)
len += scnprintf(buf + len, sizeof(buf) - len, " %s", id->id);
- pnp_dbg(&dev->dev, "%s device, IDs%s (%s)\n",
- dev->protocol->name, buf, dev->active ? "active" : "disabled");
+ dev_printk(KERN_DEBUG, &dev->dev, "%s device, IDs%s (%s)\n",
+ dev->protocol->name, buf,
+ dev->active ? "active" : "disabled");
return 0;
}
diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c
index cd11b113494f..d1dbb9df53fa 100644
--- a/drivers/pnp/driver.c
+++ b/drivers/pnp/driver.c
@@ -236,7 +236,7 @@ void pnp_unregister_driver(struct pnp_driver *drv)
* @dev: pointer to the desired device
* @id: pointer to an EISA id string
*/
-struct pnp_id *pnp_add_id(struct pnp_dev *dev, char *id)
+struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id)
{
struct pnp_id *dev_id, *ptr;
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index dc4e32e031e9..2d73dfcecdbb 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -28,7 +28,7 @@
#include "../base.h"
#include "pnpacpi.h"
-static int num = 0;
+static int num;
/* We need only to blacklist devices that have already an acpi driver that
* can't use pnp layer. We don't need to blacklist device that are directly
@@ -59,7 +59,7 @@ static inline int __init is_exclusive_device(struct acpi_device *dev)
#define TEST_ALPHA(c) \
if (!('@' <= (c) || (c) <= 'Z')) \
return 0
-static int __init ispnpidacpi(char *id)
+static int __init ispnpidacpi(const char *id)
{
TEST_ALPHA(id[0]);
TEST_ALPHA(id[1]);
@@ -180,11 +180,24 @@ struct pnp_protocol pnpacpi_protocol = {
};
EXPORT_SYMBOL(pnpacpi_protocol);
+static char *pnpacpi_get_id(struct acpi_device *device)
+{
+ struct acpi_hardware_id *id;
+
+ list_for_each_entry(id, &device->pnp.ids, list) {
+ if (ispnpidacpi(id->id))
+ return id->id;
+ }
+
+ return NULL;
+}
+
static int __init pnpacpi_add_device(struct acpi_device *device)
{
acpi_handle temp = NULL;
acpi_status status;
struct pnp_dev *dev;
+ char *pnpid;
struct acpi_hardware_id *id;
/*
@@ -192,11 +205,17 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
* driver should not be loaded.
*/
status = acpi_get_handle(device->handle, "_CRS", &temp);
- if (ACPI_FAILURE(status) || !ispnpidacpi(acpi_device_hid(device)) ||
- is_exclusive_device(device) || (!device->status.present))
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ pnpid = pnpacpi_get_id(device);
+ if (!pnpid)
+ return 0;
+
+ if (is_exclusive_device(device) || !device->status.present)
return 0;
- dev = pnp_alloc_dev(&pnpacpi_protocol, num, acpi_device_hid(device));
+ dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid);
if (!dev)
return -ENOMEM;
@@ -227,7 +246,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
pnpacpi_parse_resource_option_data(dev);
list_for_each_entry(id, &device->pnp.ids, list) {
- if (!strcmp(id->id, acpi_device_hid(device)))
+ if (!strcmp(id->id, pnpid))
continue;
if (!ispnpidacpi(id->id))
continue;
diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c
index e3446ab8b563..a925e6b63d72 100644
--- a/drivers/pnp/resource.c
+++ b/drivers/pnp/resource.c
@@ -523,7 +523,7 @@ struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq,
res->start = irq;
res->end = irq;
- pnp_dbg(&dev->dev, " add %pr\n", res);
+ dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
return pnp_res;
}
@@ -544,7 +544,7 @@ struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma,
res->start = dma;
res->end = dma;
- pnp_dbg(&dev->dev, " add %pr\n", res);
+ dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
return pnp_res;
}
@@ -568,7 +568,7 @@ struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev,
res->start = start;
res->end = end;
- pnp_dbg(&dev->dev, " add %pr\n", res);
+ dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
return pnp_res;
}
@@ -592,7 +592,7 @@ struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev,
res->start = start;
res->end = end;
- pnp_dbg(&dev->dev, " add %pr\n", res);
+ dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
return pnp_res;
}
@@ -616,7 +616,7 @@ struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev,
res->start = start;
res->end = end;
- pnp_dbg(&dev->dev, " add %pr\n", res);
+ dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res);
return pnp_res;
}
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 07343568a12e..60d83d983a36 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -64,8 +64,7 @@ config TEST_POWER
config BATTERY_DS2760
tristate "DS2760 battery driver (HP iPAQ & others)"
- select W1
- select W1_SLAVE_DS2760
+ depends on W1 && W1_SLAVE_DS2760
help
Say Y here to enable support for batteries with ds2760 chip.
@@ -109,6 +108,13 @@ config BATTERY_WM97XX
help
Say Y to enable support for battery measured by WM97xx aux port.
+config BATTERY_BQ20Z75
+ tristate "TI BQ20z75 gas gauge"
+ depends on I2C
+ help
+ Say Y to include support for TI BQ20z75 SBS-compliant
+ gas gauge and protection IC.
+
config BATTERY_BQ27x00
tristate "BQ27x00 battery driver"
depends on I2C
@@ -166,4 +172,17 @@ config BATTERY_INTEL_MID
Say Y here to enable the battery driver on Intel MID
platforms.
+config CHARGER_ISP1704
+ tristate "ISP1704 USB Charger Detection"
+ depends on USB_OTG_UTILS
+ help
+ Say Y to enable support for USB Charger Detection with
+ ISP1707/ISP1704 USB transceivers.
+
+config CHARGER_TWL4030
+ tristate "OMAP TWL4030 BCI charger driver"
+ depends on TWL4030_CORE
+ help
+ Say Y here to enable support for TWL4030 Battery Charge Interface.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 10143aaf4ee3..c75772eb157c 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -1,16 +1,8 @@
-power_supply-objs := power_supply_core.o
+ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG
-ifeq ($(CONFIG_SYSFS),y)
-power_supply-objs += power_supply_sysfs.o
-endif
-
-ifeq ($(CONFIG_LEDS_TRIGGERS),y)
-power_supply-objs += power_supply_leds.o
-endif
-
-ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
+power_supply-y := power_supply_core.o
+power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o
+power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
@@ -29,6 +21,7 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
+obj-$(CONFIG_BATTERY_BQ20Z75) += bq20z75.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
@@ -37,3 +30,5 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
+obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
+obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c
new file mode 100644
index 000000000000..492da27e1a47
--- /dev/null
+++ b/drivers/power/bq20z75.c
@@ -0,0 +1,493 @@
+/*
+ * Gas Gauge driver for TI's BQ20Z75
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+enum {
+ REG_MANUFACTURER_DATA,
+ REG_TEMPERATURE,
+ REG_VOLTAGE,
+ REG_CURRENT,
+ REG_CAPACITY,
+ REG_TIME_TO_EMPTY,
+ REG_TIME_TO_FULL,
+ REG_STATUS,
+ REG_CYCLE_COUNT,
+ REG_SERIAL_NUMBER,
+ REG_REMAINING_CAPACITY,
+ REG_FULL_CHARGE_CAPACITY,
+ REG_DESIGN_CAPACITY,
+ REG_DESIGN_VOLTAGE,
+};
+
+/* manufacturer access defines */
+#define MANUFACTURER_ACCESS_STATUS 0x0006
+#define MANUFACTURER_ACCESS_SLEEP 0x0011
+
+/* battery status value bits */
+#define BATTERY_DISCHARGING 0x40
+#define BATTERY_FULL_CHARGED 0x20
+#define BATTERY_FULL_DISCHARGED 0x10
+
+#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \
+ .psp = _psp, \
+ .addr = _addr, \
+ .min_value = _min_value, \
+ .max_value = _max_value, \
+}
+
+static const struct bq20z75_device_data {
+ enum power_supply_property psp;
+ u8 addr;
+ int min_value;
+ int max_value;
+} bq20z75_data[] = {
+ [REG_MANUFACTURER_DATA] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
+ [REG_TEMPERATURE] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
+ [REG_VOLTAGE] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
+ [REG_CURRENT] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768,
+ 32767),
+ [REG_CAPACITY] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
+ [REG_REMAINING_CAPACITY] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
+ [REG_FULL_CHARGE_CAPACITY] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
+ [REG_TIME_TO_EMPTY] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0,
+ 65535),
+ [REG_TIME_TO_FULL] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0,
+ 65535),
+ [REG_STATUS] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
+ [REG_CYCLE_COUNT] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
+ [REG_DESIGN_CAPACITY] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0,
+ 65535),
+ [REG_DESIGN_VOLTAGE] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0,
+ 65535),
+ [REG_SERIAL_NUMBER] =
+ BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
+};
+
+static enum power_supply_property bq20z75_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+};
+
+struct bq20z75_info {
+ struct i2c_client *client;
+ struct power_supply power_supply;
+};
+
+static int bq20z75_read_word_data(struct i2c_client *client, u8 address)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_word_data(client, address);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: i2c read at address 0x%x failed\n",
+ __func__, address);
+ return ret;
+ }
+ return le16_to_cpu(ret);
+}
+
+static int bq20z75_write_word_data(struct i2c_client *client, u8 address,
+ u16 value)
+{
+ s32 ret;
+
+ ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: i2c write to address 0x%x failed\n",
+ __func__, address);
+ return ret;
+ }
+ return 0;
+}
+
+static int bq20z75_get_battery_presence_and_health(
+ struct i2c_client *client, enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ s32 ret;
+
+ /* Write to ManufacturerAccess with
+ * ManufacturerAccess command and then
+ * read the status */
+ ret = bq20z75_write_word_data(client,
+ bq20z75_data[REG_MANUFACTURER_DATA].addr,
+ MANUFACTURER_ACCESS_STATUS);
+ if (ret < 0)
+ return ret;
+
+
+ ret = bq20z75_read_word_data(client,
+ bq20z75_data[REG_MANUFACTURER_DATA].addr);
+ if (ret < 0)
+ return ret;
+
+ if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value ||
+ ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) {
+ val->intval = 0;
+ return 0;
+ }
+
+ /* Mask the upper nibble of 2nd byte and
+ * lower byte of response then
+ * shift the result by 8 to get status*/
+ ret &= 0x0F00;
+ ret >>= 8;
+ if (psp == POWER_SUPPLY_PROP_PRESENT) {
+ if (ret == 0x0F)
+ /* battery removed */
+ val->intval = 0;
+ else
+ val->intval = 1;
+ } else if (psp == POWER_SUPPLY_PROP_HEALTH) {
+ if (ret == 0x09)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ else if (ret == 0x0B)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (ret == 0x0C)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ }
+
+ return 0;
+}
+
+static int bq20z75_get_battery_property(struct i2c_client *client,
+ int reg_offset, enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ s32 ret;
+
+ ret = bq20z75_read_word_data(client,
+ bq20z75_data[reg_offset].addr);
+ if (ret < 0)
+ return ret;
+
+ /* returned values are 16 bit */
+ if (bq20z75_data[reg_offset].min_value < 0)
+ ret = (s16)ret;
+
+ if (ret >= bq20z75_data[reg_offset].min_value &&
+ ret <= bq20z75_data[reg_offset].max_value) {
+ val->intval = ret;
+ if (psp == POWER_SUPPLY_PROP_STATUS) {
+ if (ret & BATTERY_FULL_CHARGED)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else if (ret & BATTERY_FULL_DISCHARGED)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (ret & BATTERY_DISCHARGING)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ } else {
+ if (psp == POWER_SUPPLY_PROP_STATUS)
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ else
+ val->intval = 0;
+ }
+
+ return 0;
+}
+
+static void bq20z75_unit_adjustment(struct i2c_client *client,
+ enum power_supply_property psp, union power_supply_propval *val)
+{
+#define BASE_UNIT_CONVERSION 1000
+#define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION)
+#define TIME_UNIT_CONVERSION 600
+#define TEMP_KELVIN_TO_CELCIUS 2731
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ val->intval *= BATTERY_MODE_CAP_MULT_WATT;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval *= BASE_UNIT_CONVERSION;
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ /* bq20z75 provides battery tempreture in 0.1°K
+ * so convert it to 0.1°C */
+ val->intval -= TEMP_KELVIN_TO_CELCIUS;
+ val->intval *= 10;
+ break;
+
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+ case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+ val->intval *= TIME_UNIT_CONVERSION;
+ break;
+
+ default:
+ dev_dbg(&client->dev,
+ "%s: no need for unit conversion %d\n", __func__, psp);
+ }
+}
+
+static int bq20z75_get_battery_capacity(struct i2c_client *client,
+ int reg_offset, enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ s32 ret;
+
+ ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr);
+ if (ret < 0)
+ return ret;
+
+ if (psp == POWER_SUPPLY_PROP_CAPACITY) {
+ /* bq20z75 spec says that this can be >100 %
+ * even if max value is 100 % */
+ val->intval = min(ret, 100);
+ } else
+ val->intval = ret;
+
+ return 0;
+}
+
+static char bq20z75_serial[5];
+static int bq20z75_get_battery_serial_number(struct i2c_client *client,
+ union power_supply_propval *val)
+{
+ int ret;
+
+ ret = bq20z75_read_word_data(client,
+ bq20z75_data[REG_SERIAL_NUMBER].addr);
+ if (ret < 0)
+ return ret;
+
+ ret = sprintf(bq20z75_serial, "%04x", ret);
+ val->strval = bq20z75_serial;
+
+ return 0;
+}
+
+static int bq20z75_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int count;
+ int ret;
+ struct bq20z75_info *bq20z75_device = container_of(psy,
+ struct bq20z75_info, power_supply);
+ struct i2c_client *client = bq20z75_device->client;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = bq20z75_get_battery_presence_and_health(client, psp, val);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) {
+ if (psp == bq20z75_data[count].psp)
+ break;
+ }
+
+ ret = bq20z75_get_battery_capacity(client, count, psp, val);
+ if (ret)
+ return ret;
+
+ break;
+
+ case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+ ret = bq20z75_get_battery_serial_number(client, val);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_TEMP:
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+ case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) {
+ if (psp == bq20z75_data[count].psp)
+ break;
+ }
+
+ ret = bq20z75_get_battery_property(client, count, psp, val);
+ if (ret)
+ return ret;
+
+ break;
+
+ default:
+ dev_err(&client->dev,
+ "%s: INVALID property\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Convert units to match requirements for power supply class */
+ bq20z75_unit_adjustment(client, psp, val);
+
+ dev_dbg(&client->dev,
+ "%s: property = %d, value = %d\n", __func__, psp, val->intval);
+
+ return 0;
+}
+
+static int bq20z75_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bq20z75_info *bq20z75_device;
+ int rc;
+
+ bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL);
+ if (!bq20z75_device)
+ return -ENOMEM;
+
+ bq20z75_device->client = client;
+ bq20z75_device->power_supply.name = "battery";
+ bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
+ bq20z75_device->power_supply.properties = bq20z75_properties;
+ bq20z75_device->power_supply.num_properties =
+ ARRAY_SIZE(bq20z75_properties);
+ bq20z75_device->power_supply.get_property = bq20z75_get_property;
+
+ i2c_set_clientdata(client, bq20z75_device);
+
+ rc = power_supply_register(&client->dev, &bq20z75_device->power_supply);
+ if (rc) {
+ dev_err(&client->dev,
+ "%s: Failed to register power supply\n", __func__);
+ kfree(bq20z75_device);
+ return rc;
+ }
+
+ dev_info(&client->dev,
+ "%s: battery gas gauge device registered\n", client->name);
+
+ return 0;
+}
+
+static int bq20z75_remove(struct i2c_client *client)
+{
+ struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
+
+ power_supply_unregister(&bq20z75_device->power_supply);
+ kfree(bq20z75_device);
+ bq20z75_device = NULL;
+
+ return 0;
+}
+
+#if defined CONFIG_PM
+static int bq20z75_suspend(struct i2c_client *client,
+ pm_message_t state)
+{
+ s32 ret;
+
+ /* write to manufacturer access with sleep command */
+ ret = bq20z75_write_word_data(client,
+ bq20z75_data[REG_MANUFACTURER_DATA].addr,
+ MANUFACTURER_ACCESS_SLEEP);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#else
+#define bq20z75_suspend NULL
+#endif
+/* any smbus transaction will wake up bq20z75 */
+#define bq20z75_resume NULL
+
+static const struct i2c_device_id bq20z75_id[] = {
+ { "bq20z75", 0 },
+ {}
+};
+
+static struct i2c_driver bq20z75_battery_driver = {
+ .probe = bq20z75_probe,
+ .remove = bq20z75_remove,
+ .suspend = bq20z75_suspend,
+ .resume = bq20z75_resume,
+ .id_table = bq20z75_id,
+ .driver = {
+ .name = "bq20z75-battery",
+ },
+};
+
+static int __init bq20z75_battery_init(void)
+{
+ return i2c_add_driver(&bq20z75_battery_driver);
+}
+module_init(bq20z75_battery_init);
+
+static void __exit bq20z75_battery_exit(void)
+{
+ i2c_del_driver(&bq20z75_battery_driver);
+}
+module_exit(bq20z75_battery_exit);
+
+MODULE_DESCRIPTION("BQ20z75 battery monitor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
index 3ec9c6a8896b..eff0273d4030 100644
--- a/drivers/power/bq27x00_battery.c
+++ b/drivers/power/bq27x00_battery.c
@@ -418,6 +418,7 @@ static int bq27x00_battery_remove(struct i2c_client *client)
power_supply_unregister(&di->bat);
+ kfree(di->bus);
kfree(di->bat.name);
mutex_lock(&battery_mutex);
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c
index 4d3b27228a2e..b3c01c16a164 100644
--- a/drivers/power/ds2760_battery.c
+++ b/drivers/power/ds2760_battery.c
@@ -586,6 +586,7 @@ static int ds2760_battery_remove(struct platform_device *pdev)
&di->set_charged_work);
destroy_workqueue(di->monitor_wqueue);
power_supply_unregister(&di->bat);
+ kfree(di);
return 0;
}
diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c
index 84d3c43cf2bc..6957e8af6449 100644
--- a/drivers/power/ds2782_battery.c
+++ b/drivers/power/ds2782_battery.c
@@ -44,8 +44,8 @@ struct ds278x_info;
struct ds278x_battery_ops {
int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
- int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uA);
- int (*get_battery_capacity)(struct ds278x_info *info, int *capacity_uA);
+ int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV);
+ int (*get_battery_capacity)(struct ds278x_info *info, int *capacity);
};
#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
@@ -137,7 +137,7 @@ static int ds2782_get_current(struct ds278x_info *info, int *current_uA)
return 0;
}
-static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA)
+static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV)
{
s16 raw;
int err;
@@ -149,7 +149,7 @@ static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA)
err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
if (err)
return err;
- *voltage_uA = (raw / 32) * 4800;
+ *voltage_uV = (raw / 32) * 4800;
return 0;
}
@@ -177,7 +177,7 @@ static int ds2786_get_current(struct ds278x_info *info, int *current_uA)
return 0;
}
-static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA)
+static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV)
{
s16 raw;
int err;
@@ -189,7 +189,7 @@ static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA)
err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
if (err)
return err;
- *voltage_uA = (raw / 8) * 1220;
+ *voltage_uV = (raw / 8) * 1220;
return 0;
}
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c
new file mode 100644
index 000000000000..72512185f3e2
--- /dev/null
+++ b/drivers/power/isp1704_charger.c
@@ -0,0 +1,369 @@
+/*
+ * ISP1704 USB Charger Detection driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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/err.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Vendor specific Power Control register */
+#define ISP1704_PWR_CTRL 0x3d
+#define ISP1704_PWR_CTRL_SWCTRL (1 << 0)
+#define ISP1704_PWR_CTRL_DET_COMP (1 << 1)
+#define ISP1704_PWR_CTRL_BVALID_RISE (1 << 2)
+#define ISP1704_PWR_CTRL_BVALID_FALL (1 << 3)
+#define ISP1704_PWR_CTRL_DP_WKPU_EN (1 << 4)
+#define ISP1704_PWR_CTRL_VDAT_DET (1 << 5)
+#define ISP1704_PWR_CTRL_DPVSRC_EN (1 << 6)
+#define ISP1704_PWR_CTRL_HWDETECT (1 << 7)
+
+#define NXP_VENDOR_ID 0x04cc
+
+static u16 isp170x_id[] = {
+ 0x1704,
+ 0x1707,
+};
+
+struct isp1704_charger {
+ struct device *dev;
+ struct power_supply psy;
+ struct otg_transceiver *otg;
+ struct notifier_block nb;
+ struct work_struct work;
+
+ char model[7];
+ unsigned present:1;
+};
+
+/*
+ * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger
+ * is actually a dedicated charger, the following steps need to be taken.
+ */
+static inline int isp1704_charger_verify(struct isp1704_charger *isp)
+{
+ int ret = 0;
+ u8 r;
+
+ /* Reset the transceiver */
+ r = otg_io_read(isp->otg, ULPI_FUNC_CTRL);
+ r |= ULPI_FUNC_CTRL_RESET;
+ otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
+ usleep_range(1000, 2000);
+
+ /* Set normal mode */
+ r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
+ otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
+
+ /* Clear the DP and DM pull-down bits */
+ r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
+ otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), r);
+
+ /* Enable strong pull-up on DP (1.5K) and reset */
+ r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
+ otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), r);
+ usleep_range(1000, 2000);
+
+ /* Read the line state */
+ if (!otg_io_read(isp->otg, ULPI_DEBUG)) {
+ /* Disable strong pull-up on DP (1.5K) */
+ otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
+ ULPI_FUNC_CTRL_TERMSELECT);
+ return 1;
+ }
+
+ /* Is it a charger or PS/2 connection */
+
+ /* Enable weak pull-up resistor on DP */
+ otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL),
+ ISP1704_PWR_CTRL_DP_WKPU_EN);
+
+ /* Disable strong pull-up on DP (1.5K) */
+ otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
+ ULPI_FUNC_CTRL_TERMSELECT);
+
+ /* Enable weak pull-down resistor on DM */
+ otg_io_write(isp->otg, ULPI_SET(ULPI_OTG_CTRL),
+ ULPI_OTG_CTRL_DM_PULLDOWN);
+
+ /* It's a charger if the line states are clear */
+ if (!(otg_io_read(isp->otg, ULPI_DEBUG)))
+ ret = 1;
+
+ /* Disable weak pull-up resistor on DP */
+ otg_io_write(isp->otg, ULPI_CLR(ISP1704_PWR_CTRL),
+ ISP1704_PWR_CTRL_DP_WKPU_EN);
+
+ return ret;
+}
+
+static inline int isp1704_charger_detect(struct isp1704_charger *isp)
+{
+ unsigned long timeout;
+ u8 r;
+ int ret = 0;
+
+ /* set SW control bit in PWR_CTRL register */
+ otg_io_write(isp->otg, ISP1704_PWR_CTRL,
+ ISP1704_PWR_CTRL_SWCTRL);
+
+ /* enable manual charger detection */
+ r = (ISP1704_PWR_CTRL_SWCTRL | ISP1704_PWR_CTRL_DPVSRC_EN);
+ otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL), r);
+ usleep_range(1000, 2000);
+
+ timeout = jiffies + msecs_to_jiffies(300);
+ do {
+ /* Check if there is a charger */
+ if (otg_io_read(isp->otg, ISP1704_PWR_CTRL)
+ & ISP1704_PWR_CTRL_VDAT_DET) {
+ ret = isp1704_charger_verify(isp);
+ break;
+ }
+ } while (!time_after(jiffies, timeout));
+
+ return ret;
+}
+
+static void isp1704_charger_work(struct work_struct *data)
+{
+ int detect;
+ struct isp1704_charger *isp =
+ container_of(data, struct isp1704_charger, work);
+
+ /*
+ * FIXME Only supporting dedicated chargers even though isp1704 can
+ * detect HUB and HOST chargers. If the device has already been
+ * enumerated, the detection will break the connection.
+ */
+ if (isp->otg->state != OTG_STATE_B_IDLE)
+ return;
+
+ /* disable data pullups */
+ if (isp->otg->gadget)
+ usb_gadget_disconnect(isp->otg->gadget);
+
+ /* detect charger */
+ detect = isp1704_charger_detect(isp);
+ if (detect) {
+ isp->present = detect;
+ power_supply_changed(&isp->psy);
+ }
+
+ /* enable data pullups */
+ if (isp->otg->gadget)
+ usb_gadget_connect(isp->otg->gadget);
+}
+
+static int isp1704_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ struct isp1704_charger *isp =
+ container_of(nb, struct isp1704_charger, nb);
+
+ switch (event) {
+ case USB_EVENT_VBUS:
+ schedule_work(&isp->work);
+ break;
+ case USB_EVENT_NONE:
+ if (isp->present) {
+ isp->present = 0;
+ power_supply_changed(&isp->psy);
+ }
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int isp1704_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct isp1704_charger *isp =
+ container_of(psy, struct isp1704_charger, psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = isp->present;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = isp->model;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "NXP";
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum power_supply_property power_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
+{
+ int vendor;
+ int product;
+ int i;
+ int ret = -ENODEV;
+
+ /* Test ULPI interface */
+ ret = otg_io_write(isp->otg, ULPI_SCRATCH, 0xaa);
+ if (ret < 0)
+ return ret;
+
+ ret = otg_io_read(isp->otg, ULPI_SCRATCH);
+ if (ret < 0)
+ return ret;
+
+ if (ret != 0xaa)
+ return -ENODEV;
+
+ /* Verify the product and vendor id matches */
+ vendor = otg_io_read(isp->otg, ULPI_VENDOR_ID_LOW);
+ vendor |= otg_io_read(isp->otg, ULPI_VENDOR_ID_HIGH) << 8;
+ if (vendor != NXP_VENDOR_ID)
+ return -ENODEV;
+
+ product = otg_io_read(isp->otg, ULPI_PRODUCT_ID_LOW);
+ product |= otg_io_read(isp->otg, ULPI_PRODUCT_ID_HIGH) << 8;
+
+ for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
+ if (product == isp170x_id[i]) {
+ sprintf(isp->model, "isp%x", product);
+ return product;
+ }
+ }
+
+ dev_err(isp->dev, "product id %x not matching known ids", product);
+
+ return -ENODEV;
+}
+
+static int __devinit isp1704_charger_probe(struct platform_device *pdev)
+{
+ struct isp1704_charger *isp;
+ int ret = -ENODEV;
+
+ isp = kzalloc(sizeof *isp, GFP_KERNEL);
+ if (!isp)
+ return -ENOMEM;
+
+ isp->otg = otg_get_transceiver();
+ if (!isp->otg)
+ goto fail0;
+
+ ret = isp1704_test_ulpi(isp);
+ if (ret < 0)
+ goto fail1;
+
+ isp->dev = &pdev->dev;
+ platform_set_drvdata(pdev, isp);
+
+ isp->psy.name = "isp1704";
+ isp->psy.type = POWER_SUPPLY_TYPE_USB;
+ isp->psy.properties = power_props;
+ isp->psy.num_properties = ARRAY_SIZE(power_props);
+ isp->psy.get_property = isp1704_charger_get_property;
+
+ ret = power_supply_register(isp->dev, &isp->psy);
+ if (ret)
+ goto fail1;
+
+ /*
+ * REVISIT: using work in order to allow the otg notifications to be
+ * made atomically in the future.
+ */
+ INIT_WORK(&isp->work, isp1704_charger_work);
+
+ isp->nb.notifier_call = isp1704_notifier_call;
+
+ ret = otg_register_notifier(isp->otg, &isp->nb);
+ if (ret)
+ goto fail2;
+
+ dev_info(isp->dev, "registered with product id %s\n", isp->model);
+
+ return 0;
+fail2:
+ power_supply_unregister(&isp->psy);
+fail1:
+ otg_put_transceiver(isp->otg);
+fail0:
+ kfree(isp);
+
+ dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
+
+ return ret;
+}
+
+static int __devexit isp1704_charger_remove(struct platform_device *pdev)
+{
+ struct isp1704_charger *isp = platform_get_drvdata(pdev);
+
+ otg_unregister_notifier(isp->otg, &isp->nb);
+ power_supply_unregister(&isp->psy);
+ otg_put_transceiver(isp->otg);
+ kfree(isp);
+
+ return 0;
+}
+
+static struct platform_driver isp1704_charger_driver = {
+ .driver = {
+ .name = "isp1704_charger",
+ },
+ .probe = isp1704_charger_probe,
+ .remove = __devexit_p(isp1704_charger_remove),
+};
+
+static int __init isp1704_charger_init(void)
+{
+ return platform_driver_register(&isp1704_charger_driver);
+}
+module_init(isp1704_charger_init);
+
+static void __exit isp1704_charger_exit(void)
+{
+ platform_driver_unregister(&isp1704_charger_driver);
+}
+module_exit(isp1704_charger_exit);
+
+MODULE_ALIAS("platform:isp1704_charger");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("ISP170x USB Charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c
index 20c4b952e9bd..a8108a73593e 100644
--- a/drivers/power/jz4740-battery.c
+++ b/drivers/power/jz4740-battery.c
@@ -383,6 +383,7 @@ static int __devexit jz_battery_remove(struct platform_device *pdev)
iounmap(jz_battery->base);
release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
+ kfree(jz_battery);
return 0;
}
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c
index aafc1c506eda..5bc1dcf7785e 100644
--- a/drivers/power/olpc_battery.c
+++ b/drivers/power/olpc_battery.c
@@ -271,14 +271,14 @@ static int olpc_bat_get_property(struct power_supply *psy,
if (ret)
return ret;
- val->intval = (int)be16_to_cpu(ec_word) * 9760L / 32;
+ val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
if (ret)
return ret;
- val->intval = (int)be16_to_cpu(ec_word) * 15625L / 120;
+ val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120;
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
@@ -299,7 +299,7 @@ static int olpc_bat_get_property(struct power_supply *psy,
if (ret)
return ret;
- val->intval = (int)be16_to_cpu(ec_word) * 100 / 256;
+ val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256;
break;
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
@@ -313,7 +313,7 @@ static int olpc_bat_get_property(struct power_supply *psy,
if (ret)
return ret;
- val->intval = (int)be16_to_cpu(ec_word) * 6250 / 15;
+ val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15;
break;
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8);
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c
index 066f994e6fe5..4fa52e1781a2 100644
--- a/drivers/power/pcf50633-charger.c
+++ b/drivers/power/pcf50633-charger.c
@@ -456,6 +456,7 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
+ sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group);
power_supply_unregister(&mbc->usb);
power_supply_unregister(&mbc->adapter);
power_supply_unregister(&mbc->ac);
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 9d30eeb8c810..cd1f90754a3a 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -42,7 +42,8 @@ static ssize_t power_supply_show_property(struct device *dev,
struct device_attribute *attr,
char *buf) {
static char *type_text[] = {
- "Battery", "UPS", "Mains", "USB"
+ "Battery", "UPS", "Mains", "USB",
+ "USB_DCP", "USB_CDP", "USB_ACA"
};
static char *status_text[] = {
"Unknown", "Charging", "Discharging", "Not charging", "Full"
@@ -138,6 +139,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(voltage_min_design),
POWER_SUPPLY_ATTR(voltage_now),
POWER_SUPPLY_ATTR(voltage_avg),
+ POWER_SUPPLY_ATTR(current_max),
POWER_SUPPLY_ATTR(current_now),
POWER_SUPPLY_ATTR(current_avg),
POWER_SUPPLY_ATTR(power_now),
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
new file mode 100644
index 000000000000..ff1f42398a2e
--- /dev/null
+++ b/drivers/power/twl4030_charger.c
@@ -0,0 +1,565 @@
+/*
+ * TWL4030/TPS65950 BCI (Battery Charger Interface) driver
+ *
+ * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com>
+ *
+ * based on twl4030_bci_battery.c by TI
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/i2c/twl.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/usb/otg.h>
+
+#define TWL4030_BCIMSTATEC 0x02
+#define TWL4030_BCIICHG 0x08
+#define TWL4030_BCIVAC 0x0a
+#define TWL4030_BCIVBUS 0x0c
+#define TWL4030_BCIMFSTS4 0x10
+#define TWL4030_BCICTL1 0x23
+
+#define TWL4030_BCIAUTOWEN BIT(5)
+#define TWL4030_CONFIG_DONE BIT(4)
+#define TWL4030_BCIAUTOUSB BIT(1)
+#define TWL4030_BCIAUTOAC BIT(0)
+#define TWL4030_CGAIN BIT(5)
+#define TWL4030_USBFASTMCHG BIT(2)
+#define TWL4030_STS_VBUS BIT(7)
+#define TWL4030_STS_USB_ID BIT(2)
+
+/* BCI interrupts */
+#define TWL4030_WOVF BIT(0) /* Watchdog overflow */
+#define TWL4030_TMOVF BIT(1) /* Timer overflow */
+#define TWL4030_ICHGHIGH BIT(2) /* Battery charge current high */
+#define TWL4030_ICHGLOW BIT(3) /* Battery cc. low / FSM state change */
+#define TWL4030_ICHGEOC BIT(4) /* Battery current end-of-charge */
+#define TWL4030_TBATOR2 BIT(5) /* Battery temperature out of range 2 */
+#define TWL4030_TBATOR1 BIT(6) /* Battery temperature out of range 1 */
+#define TWL4030_BATSTS BIT(7) /* Battery status */
+
+#define TWL4030_VBATLVL BIT(0) /* VBAT level */
+#define TWL4030_VBATOV BIT(1) /* VBAT overvoltage */
+#define TWL4030_VBUSOV BIT(2) /* VBUS overvoltage */
+#define TWL4030_ACCHGOV BIT(3) /* Ac charger overvoltage */
+
+#define TWL4030_MSTATEC_USB BIT(4)
+#define TWL4030_MSTATEC_AC BIT(5)
+#define TWL4030_MSTATEC_MASK 0x0f
+#define TWL4030_MSTATEC_QUICK1 0x02
+#define TWL4030_MSTATEC_QUICK7 0x07
+#define TWL4030_MSTATEC_COMPLETE1 0x0b
+#define TWL4030_MSTATEC_COMPLETE4 0x0e
+
+static bool allow_usb;
+module_param(allow_usb, bool, 1);
+MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
+
+struct twl4030_bci {
+ struct device *dev;
+ struct power_supply ac;
+ struct power_supply usb;
+ struct otg_transceiver *transceiver;
+ struct notifier_block otg_nb;
+ int irq_chg;
+ int irq_bci;
+};
+
+/*
+ * clear and set bits on an given register on a given module
+ */
+static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
+{
+ u8 val = 0;
+ int ret;
+
+ ret = twl_i2c_read_u8(mod_no, &val, reg);
+ if (ret)
+ return ret;
+
+ val &= ~clear;
+ val |= set;
+
+ return twl_i2c_write_u8(mod_no, val, reg);
+}
+
+static int twl4030_bci_read(u8 reg, u8 *val)
+{
+ return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg);
+}
+
+static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
+{
+ return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0,
+ TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
+ TWL4030_PM_MASTER_BOOT_BCI);
+}
+
+static int twl4030bci_read_adc_val(u8 reg)
+{
+ int ret, temp;
+ u8 val;
+
+ /* read MSB */
+ ret = twl4030_bci_read(reg + 1, &val);
+ if (ret)
+ return ret;
+
+ temp = (int)(val & 0x03) << 8;
+
+ /* read LSB */
+ ret = twl4030_bci_read(reg, &val);
+ if (ret)
+ return ret;
+
+ return temp | val;
+}
+
+/*
+ * Check if VBUS power is present
+ */
+static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
+{
+ int ret;
+ u8 hwsts;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts,
+ TWL4030_PM_MASTER_STS_HW_CONDITIONS);
+ if (ret < 0)
+ return 0;
+
+ dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts);
+
+ /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */
+ if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Enable/Disable USB Charge funtionality.
+ */
+static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
+{
+ int ret;
+
+ if (enable) {
+ /* Check for USB charger conneted */
+ if (!twl4030_bci_have_vbus(bci))
+ return -ENODEV;
+
+ /*
+ * Until we can find out what current the device can provide,
+ * require a module param to enable USB charging.
+ */
+ if (!allow_usb) {
+ dev_warn(bci->dev, "USB charging is disabled.\n");
+ return -EACCES;
+ }
+
+ /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
+ ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
+ if (ret < 0)
+ return ret;
+
+ /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
+ ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0,
+ TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
+ } else {
+ ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
+ }
+
+ return ret;
+}
+
+/*
+ * Enable/Disable AC Charge funtionality.
+ */
+static int twl4030_charger_enable_ac(bool enable)
+{
+ int ret;
+
+ if (enable)
+ ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
+ else
+ ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0);
+
+ return ret;
+}
+
+/*
+ * TWL4030 CHG_PRES (AC charger presence) events
+ */
+static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
+{
+ struct twl4030_bci *bci = arg;
+
+ dev_dbg(bci->dev, "CHG_PRES irq\n");
+ power_supply_changed(&bci->ac);
+ power_supply_changed(&bci->usb);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * TWL4030 BCI monitoring events
+ */
+static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
+{
+ struct twl4030_bci *bci = arg;
+ u8 irqs1, irqs2;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1,
+ TWL4030_INTERRUPTS_BCIISR1A);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2,
+ TWL4030_INTERRUPTS_BCIISR2A);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1);
+
+ if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) {
+ /* charger state change, inform the core */
+ power_supply_changed(&bci->ac);
+ power_supply_changed(&bci->usb);
+ }
+
+ /* various monitoring events, for now we just log them here */
+ if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
+ dev_warn(bci->dev, "battery temperature out of range\n");
+
+ if (irqs1 & TWL4030_BATSTS)
+ dev_crit(bci->dev, "battery disconnected\n");
+
+ if (irqs2 & TWL4030_VBATOV)
+ dev_crit(bci->dev, "VBAT overvoltage\n");
+
+ if (irqs2 & TWL4030_VBUSOV)
+ dev_crit(bci->dev, "VBUS overvoltage\n");
+
+ if (irqs2 & TWL4030_ACCHGOV)
+ dev_crit(bci->dev, "Ac charger overvoltage\n");
+
+ return IRQ_HANDLED;
+}
+
+static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
+ void *priv)
+{
+ struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb);
+
+ dev_dbg(bci->dev, "OTG notify %lu\n", val);
+
+ switch (val) {
+ case USB_EVENT_VBUS:
+ case USB_EVENT_CHARGER:
+ twl4030_charger_enable_usb(bci, true);
+ break;
+ case USB_EVENT_NONE:
+ twl4030_charger_enable_usb(bci, false);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+/*
+ * TI provided formulas:
+ * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
+ * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
+ * Here we use integer approximation of:
+ * CGAIN == 0: val * 1.6618 - 0.85
+ * CGAIN == 1: (val * 1.6618 - 0.85) * 2
+ */
+static int twl4030_charger_get_current(void)
+{
+ int curr;
+ int ret;
+ u8 bcictl1;
+
+ curr = twl4030bci_read_adc_val(TWL4030_BCIICHG);
+ if (curr < 0)
+ return curr;
+
+ ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+ if (ret)
+ return ret;
+
+ ret = (curr * 16618 - 850 * 10000) / 10;
+ if (bcictl1 & TWL4030_CGAIN)
+ ret *= 2;
+
+ return ret;
+}
+
+/*
+ * Returns the main charge FSM state
+ * Or < 0 on failure.
+ */
+static int twl4030bci_state(struct twl4030_bci *bci)
+{
+ int ret;
+ u8 state;
+
+ ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state);
+ if (ret) {
+ pr_err("twl4030_bci: error reading BCIMSTATEC\n");
+ return ret;
+ }
+
+ dev_dbg(bci->dev, "state: %02x\n", state);
+
+ return state;
+}
+
+static int twl4030_bci_state_to_status(int state)
+{
+ state &= TWL4030_MSTATEC_MASK;
+ if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7)
+ return POWER_SUPPLY_STATUS_CHARGING;
+ else if (TWL4030_MSTATEC_COMPLETE1 <= state &&
+ state <= TWL4030_MSTATEC_COMPLETE4)
+ return POWER_SUPPLY_STATUS_FULL;
+ else
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
+}
+
+static int twl4030_bci_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent);
+ int is_charging;
+ int state;
+ int ret;
+
+ state = twl4030bci_state(bci);
+ if (state < 0)
+ return state;
+
+ if (psy->type == POWER_SUPPLY_TYPE_USB)
+ is_charging = state & TWL4030_MSTATEC_USB;
+ else
+ is_charging = state & TWL4030_MSTATEC_AC;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (is_charging)
+ val->intval = twl4030_bci_state_to_status(state);
+ else
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ /* charging must be active for meaningful result */
+ if (!is_charging)
+ return -ENODATA;
+ if (psy->type == POWER_SUPPLY_TYPE_USB) {
+ ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
+ if (ret < 0)
+ return ret;
+ /* BCIVBUS uses ADCIN8, 7/1023 V/step */
+ val->intval = ret * 6843;
+ } else {
+ ret = twl4030bci_read_adc_val(TWL4030_BCIVAC);
+ if (ret < 0)
+ return ret;
+ /* BCIVAC uses ADCIN11, 10/1023 V/step */
+ val->intval = ret * 9775;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ if (!is_charging)
+ return -ENODATA;
+ /* current measurement is shared between AC and USB */
+ ret = twl4030_charger_get_current();
+ if (ret < 0)
+ return ret;
+ val->intval = ret;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = is_charging &&
+ twl4030_bci_state_to_status(state) !=
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property twl4030_charger_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int __init twl4030_bci_probe(struct platform_device *pdev)
+{
+ struct twl4030_bci *bci;
+ int ret;
+ int reg;
+
+ bci = kzalloc(sizeof(*bci), GFP_KERNEL);
+ if (bci == NULL)
+ return -ENOMEM;
+
+ bci->dev = &pdev->dev;
+ bci->irq_chg = platform_get_irq(pdev, 0);
+ bci->irq_bci = platform_get_irq(pdev, 1);
+
+ platform_set_drvdata(pdev, bci);
+
+ bci->ac.name = "twl4030_ac";
+ bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
+ bci->ac.properties = twl4030_charger_props;
+ bci->ac.num_properties = ARRAY_SIZE(twl4030_charger_props);
+ bci->ac.get_property = twl4030_bci_get_property;
+
+ ret = power_supply_register(&pdev->dev, &bci->ac);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
+ goto fail_register_ac;
+ }
+
+ bci->usb.name = "twl4030_usb";
+ bci->usb.type = POWER_SUPPLY_TYPE_USB;
+ bci->usb.properties = twl4030_charger_props;
+ bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props);
+ bci->usb.get_property = twl4030_bci_get_property;
+
+ ret = power_supply_register(&pdev->dev, &bci->usb);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
+ goto fail_register_usb;
+ }
+
+ ret = request_threaded_irq(bci->irq_chg, NULL,
+ twl4030_charger_interrupt, 0, pdev->name, bci);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not request irq %d, status %d\n",
+ bci->irq_chg, ret);
+ goto fail_chg_irq;
+ }
+
+ ret = request_threaded_irq(bci->irq_bci, NULL,
+ twl4030_bci_interrupt, 0, pdev->name, bci);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not request irq %d, status %d\n",
+ bci->irq_bci, ret);
+ goto fail_bci_irq;
+ }
+
+ bci->transceiver = otg_get_transceiver();
+ if (bci->transceiver != NULL) {
+ bci->otg_nb.notifier_call = twl4030_bci_usb_ncb;
+ otg_register_notifier(bci->transceiver, &bci->otg_nb);
+ }
+
+ /* Enable interrupts now. */
+ reg = ~(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
+ TWL4030_TBATOR1 | TWL4030_BATSTS);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
+ TWL4030_INTERRUPTS_BCIIMR1A);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
+ goto fail_unmask_interrupts;
+ }
+
+ reg = ~(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
+ TWL4030_INTERRUPTS_BCIIMR2A);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
+
+ twl4030_charger_enable_ac(true);
+ twl4030_charger_enable_usb(bci, true);
+
+ return 0;
+
+fail_unmask_interrupts:
+ if (bci->transceiver != NULL) {
+ otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
+ otg_put_transceiver(bci->transceiver);
+ }
+ free_irq(bci->irq_bci, bci);
+fail_bci_irq:
+ free_irq(bci->irq_chg, bci);
+fail_chg_irq:
+ power_supply_unregister(&bci->usb);
+fail_register_usb:
+ power_supply_unregister(&bci->ac);
+fail_register_ac:
+ platform_set_drvdata(pdev, NULL);
+ kfree(bci);
+
+ return ret;
+}
+
+static int __exit twl4030_bci_remove(struct platform_device *pdev)
+{
+ struct twl4030_bci *bci = platform_get_drvdata(pdev);
+
+ twl4030_charger_enable_ac(false);
+ twl4030_charger_enable_usb(bci, false);
+
+ /* mask interrupts */
+ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
+ TWL4030_INTERRUPTS_BCIIMR1A);
+ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
+ TWL4030_INTERRUPTS_BCIIMR2A);
+
+ if (bci->transceiver != NULL) {
+ otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
+ otg_put_transceiver(bci->transceiver);
+ }
+ free_irq(bci->irq_bci, bci);
+ free_irq(bci->irq_chg, bci);
+ power_supply_unregister(&bci->usb);
+ power_supply_unregister(&bci->ac);
+ platform_set_drvdata(pdev, NULL);
+ kfree(bci);
+
+ return 0;
+}
+
+static struct platform_driver twl4030_bci_driver = {
+ .driver = {
+ .name = "twl4030_bci",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(twl4030_bci_remove),
+};
+
+static int __init twl4030_bci_init(void)
+{
+ return platform_driver_probe(&twl4030_bci_driver, twl4030_bci_probe);
+}
+module_init(twl4030_bci_init);
+
+static void __exit twl4030_bci_exit(void)
+{
+ platform_driver_unregister(&twl4030_bci_driver);
+}
+module_exit(twl4030_bci_exit);
+
+MODULE_AUTHOR("Gražydas Ignotas");
+MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl4030_bci");
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c
index fbcc36dae470..ddf8cf5f3204 100644
--- a/drivers/power/wm831x_power.c
+++ b/drivers/power/wm831x_power.c
@@ -267,7 +267,6 @@ static void wm831x_config_battery(struct wm831x *wm831x)
ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
WM831X_CHG_ENA_MASK |
WM831X_CHG_FAST_MASK |
- WM831X_CHG_ITERM_MASK |
WM831X_CHG_ITERM_MASK,
reg1);
if (ret != 0)
@@ -612,6 +611,7 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev)
power_supply_unregister(&wm831x_power->battery);
power_supply_unregister(&wm831x_power->wall);
power_supply_unregister(&wm831x_power->usb);
+ kfree(wm831x_power);
return 0;
}
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 48ca7132cc05..2785a0f16c9f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -765,15 +765,15 @@ config RTC_DRV_AT32AP700X
AT32AP700x family processors.
config RTC_DRV_AT91RM9200
- tristate "AT91RM9200 or AT91SAM9RL"
- depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL
+ tristate "AT91RM9200 or some AT91SAM9 RTC"
+ depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
Driver for the internal RTC (Realtime Clock) module found on
- Atmel AT91RM9200's and AT91SAM9RL chips. On SAM9RL chips
+ Atmel AT91RM9200's and some AT91SAM9 chips. On AT91SAM9 chips
this is powered by the backup power supply.
config RTC_DRV_AT91SAM9
- tristate "AT91SAM9x/AT91CAP9"
+ tristate "AT91SAM9x/AT91CAP9 RTT as RTC"
depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40)
help
RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT
@@ -781,8 +781,8 @@ config RTC_DRV_AT91SAM9
supply (such as a small coin cell battery), but do not need to
be used as RTCs.
- (On AT91SAM9rl chips you probably want to use the dedicated RTC
- module and leave the RTT available for other uses.)
+ (On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the
+ dedicated RTC module and leave the RTT available for other uses.)
config RTC_DRV_AT91SAM9_RTT
int
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index aa95f1001761..fb613d70c2cb 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1099,16 +1099,30 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
cqr = (struct dasd_ccw_req *) intparm;
if (!cqr || ((scsw_cc(&irb->scsw) == 1) &&
(scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) &&
- (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))) {
+ ((scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND) ||
+ (scsw_stctl(&irb->scsw) == (SCSW_STCTL_STATUS_PEND |
+ SCSW_STCTL_ALERT_STATUS))))) {
if (cqr && cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
+ if (cqr)
+ memcpy(&cqr->irb, irb, sizeof(*irb));
device = dasd_device_from_cdev_locked(cdev);
- if (!IS_ERR(device)) {
- dasd_device_clear_timer(device);
- device->discipline->handle_unsolicited_interrupt(device,
- irb);
+ if (IS_ERR(device))
+ return;
+ /* ignore unsolicited interrupts for DIAG discipline */
+ if (device->discipline == dasd_diag_discipline_pointer) {
dasd_put_device(device);
+ return;
}
+ device->discipline->dump_sense_dbf(device, irb,
+ "unsolicited");
+ if ((device->features & DASD_FEATURE_ERPLOG))
+ device->discipline->dump_sense(device, cqr,
+ irb);
+ dasd_device_clear_timer(device);
+ device->discipline->handle_unsolicited_interrupt(device,
+ irb);
+ dasd_put_device(device);
return;
}
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index e82d427ff5eb..968c76cf7127 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -221,6 +221,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
ccw->cmd_code = CCW_CMD_DCTL;
ccw->count = 4;
ccw->cda = (__u32)(addr_t) DCTL_data;
+ dctl_cqr->flags = erp->flags;
dctl_cqr->function = dasd_3990_erp_DCTL;
dctl_cqr->refers = erp;
dctl_cqr->startdev = device;
@@ -1710,6 +1711,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
ccw->cda = cpa;
/* fill erp related fields */
+ erp->flags = default_erp->flags;
erp->function = dasd_3990_erp_action_1B_32;
erp->refers = default_erp->refers;
erp->startdev = device;
@@ -2354,6 +2356,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
ccw->cda = (long)(cqr->cpaddr);
}
+ erp->flags = cqr->flags;
erp->function = dasd_3990_erp_add_erp;
erp->refers = cqr;
erp->startdev = device;
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 2b3bc3ec0541..266b34b55403 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -228,25 +228,22 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr)
}
/* Handle external interruption. */
-static void
-dasd_ext_handler(__u16 code)
+static void dasd_ext_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
struct dasd_ccw_req *cqr, *next;
struct dasd_device *device;
unsigned long long expires;
unsigned long flags;
- u8 int_code, status;
addr_t ip;
int rc;
- int_code = *((u8 *) DASD_DIAG_LC_INT_CODE);
- status = *((u8 *) DASD_DIAG_LC_INT_STATUS);
- switch (int_code) {
+ switch (ext_int_code >> 24) {
case DASD_DIAG_CODE_31BIT:
- ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT);
+ ip = (addr_t) param32;
break;
case DASD_DIAG_CODE_64BIT:
- ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT);
+ ip = (addr_t) param64;
break;
default:
return;
@@ -281,7 +278,7 @@ dasd_ext_handler(__u16 code)
cqr->stopclk = get_clock();
expires = 0;
- if (status == 0) {
+ if ((ext_int_code & 0xff0000) == 0) {
cqr->status = DASD_CQR_SUCCESS;
/* Start first request on queue if possible -> fast_io. */
if (!list_empty(&device->ccw_queue)) {
@@ -296,8 +293,8 @@ dasd_ext_handler(__u16 code)
} else {
cqr->status = DASD_CQR_QUEUED;
DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for "
- "request %p was %d (%d retries left)", cqr, status,
- cqr->retries);
+ "request %p was %d (%d retries left)", cqr,
+ (ext_int_code >> 16) & 0xff, cqr->retries);
dasd_diag_erp(device);
}
diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h
index b8c78267ff3e..4f71fbe60c82 100644
--- a/drivers/s390/block/dasd_diag.h
+++ b/drivers/s390/block/dasd_diag.h
@@ -18,10 +18,6 @@
#define DEV_CLASS_FBA 0x01
#define DEV_CLASS_ECKD 0x04
-#define DASD_DIAG_LC_INT_CODE 132
-#define DASD_DIAG_LC_INT_STATUS 133
-#define DASD_DIAG_LC_INT_PARM_31BIT 128
-#define DASD_DIAG_LC_INT_PARM_64BIT 4536
#define DASD_DIAG_CODE_31BIT 0x03
#define DASD_DIAG_CODE_64BIT 0x07
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 59b4ecfb967b..50cf96389d2c 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1776,13 +1776,13 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
}
/* summary unit check */
- if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) &&
- (irb->ecw[7] == 0x0D)) {
+ sense = dasd_get_sense(irb);
+ if (sense && (sense[7] == 0x0D) &&
+ (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
dasd_alias_handle_summary_unit_check(device, irb);
return;
}
- sense = dasd_get_sense(irb);
/* service information message SIM */
if (sense && !(sense[27] & DASD_SENSE_BIT_0) &&
((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) {
@@ -1791,26 +1791,15 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
return;
}
- if ((scsw_cc(&irb->scsw) == 1) &&
- (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) &&
- (scsw_actl(&irb->scsw) & SCSW_ACTL_START_PEND) &&
- (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND)) {
+ if ((scsw_cc(&irb->scsw) == 1) && !sense &&
+ (scsw_fctl(&irb->scsw) == SCSW_FCTL_START_FUNC) &&
+ (scsw_actl(&irb->scsw) == SCSW_ACTL_START_PEND) &&
+ (scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND)) {
/* fake irb do nothing, they are handled elsewhere */
dasd_schedule_device_bh(device);
return;
}
- if (!sense) {
- /* just report other unsolicited interrupts */
- DBF_DEV_EVENT(DBF_ERR, device, "%s",
- "unsolicited interrupt received");
- } else {
- DBF_DEV_EVENT(DBF_ERR, device, "%s",
- "unsolicited interrupt received "
- "(sense available)");
- device->discipline->dump_sense_dbf(device, irb, "unsolicited");
- }
-
dasd_schedule_device_bh(device);
return;
};
@@ -3093,19 +3082,19 @@ dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb,
char *reason)
{
u64 *sense;
+ u64 *stat;
sense = (u64 *) dasd_get_sense(irb);
+ stat = (u64 *) &irb->scsw;
if (sense) {
- DBF_DEV_EVENT(DBF_EMERG, device,
- "%s: %s %02x%02x%02x %016llx %016llx %016llx "
- "%016llx", reason,
- scsw_is_tm(&irb->scsw) ? "t" : "c",
- scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw),
- scsw_dstat(&irb->scsw), sense[0], sense[1],
- sense[2], sense[3]);
+ DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : "
+ "%016llx %016llx %016llx %016llx",
+ reason, *stat, *((u32 *) (stat + 1)),
+ sense[0], sense[1], sense[2], sense[3]);
} else {
- DBF_DEV_EVENT(DBF_EMERG, device, "%s",
- "SORRY - NO VALID SENSE AVAILABLE\n");
+ DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s",
+ reason, *stat, *((u32 *) (stat + 1)),
+ "NO VALID SENSE");
}
}
@@ -3131,9 +3120,12 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d\n",
- req, scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
- scsw_cc(&irb->scsw), req ? req->intrc : 0);
+ " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
+ "CS:%02X RC:%d\n",
+ req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
+ scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
+ scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
+ req ? req->intrc : 0);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p\n",
dev_name(&device->cdev->dev),
@@ -3234,11 +3226,13 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d "
- "fcxs: 0x%02X schxs: 0x%02X\n", req,
- scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
- scsw_cc(&irb->scsw), req->intrc,
- irb->scsw.tm.fcxs, irb->scsw.tm.schxs);
+ " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
+ "CS:%02X fcxs:%02X schxs:%02X RC:%d\n",
+ req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
+ scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
+ scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
+ irb->scsw.tm.fcxs, irb->scsw.tm.schxs,
+ req ? req->intrc : 0);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing TCW: %p\n",
dev_name(&device->cdev->dev),
@@ -3246,7 +3240,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
tsb = NULL;
sense = NULL;
- if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs == 0x01))
+ if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01))
tsb = tcw_get_tsb(
(struct tcw *)(unsigned long)irb->scsw.tm.tcw);
@@ -3344,7 +3338,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
static void dasd_eckd_dump_sense(struct dasd_device *device,
struct dasd_ccw_req *req, struct irb *irb)
{
- if (req && scsw_is_tm(&req->irb.scsw))
+ if (scsw_is_tm(&irb->scsw))
dasd_eckd_dump_sense_tcw(device, req, irb);
else
dasd_eckd_dump_sense_ccw(device, req, irb);
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 2eb025592809..c4a6a31bd9cd 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -251,7 +251,6 @@ static ssize_t dasd_stats_proc_write(struct file *file,
buffer = dasd_get_user_string(user_buf, user_len);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
- DBF_EVENT(DBF_DEBUG, "/proc/dasd/statictics: '%s'\n", buffer);
/* check for valid verbs */
str = skip_spaces(buffer);
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 5707a80b96b6..35cc4686b99b 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -395,16 +395,16 @@ __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(__u16 code)
+static void sclp_interrupt_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
struct sclp_req *req;
u32 finished_sccb;
u32 evbuf_pending;
spin_lock(&sclp_lock);
- finished_sccb = S390_lowcore.ext_params & 0xfffffff8;
- evbuf_pending = S390_lowcore.ext_params & 0x3;
+ finished_sccb = param32 & 0xfffffff8;
+ evbuf_pending = param32 & 0x3;
if (finished_sccb) {
del_timer(&sclp_request_timer);
sclp_running_state = sclp_running_state_reset_pending;
@@ -819,12 +819,12 @@ EXPORT_SYMBOL(sclp_reactivate);
/* Handler for external interruption used during initialization. Modify
* request state to done. */
-static void
-sclp_check_handler(__u16 code)
+static void sclp_check_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
u32 finished_sccb;
- finished_sccb = S390_lowcore.ext_params & 0xfffffff8;
+ finished_sccb = param32 & 0xfffffff8;
/* Is this the interrupt we are waiting for? */
if (finished_sccb == 0)
return;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 0d6dc4b92cc2..9f661426e4a1 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -215,7 +215,7 @@ static void vmlogrdr_iucv_message_pending(struct iucv_path *path,
static int vmlogrdr_get_recording_class_AB(void)
{
- char cp_command[]="QUERY COMMAND RECORDING ";
+ static const char cp_command[] = "QUERY COMMAND RECORDING ";
char cp_response[80];
char *tail;
int len,i;
@@ -638,7 +638,7 @@ static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver,
char *buf)
{
- char cp_command[] = "QUERY RECORDING ";
+ static const char cp_command[] = "QUERY RECORDING ";
int len;
cpcmd(cp_command, buf, 4096, NULL);
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 13cb60162e42..76058a5166ed 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -79,17 +79,15 @@ static int pure_hex(char **cp, unsigned int *val, int min_digit,
int max_digit, int max_val)
{
int diff;
- unsigned int value;
diff = 0;
*val = 0;
- while (isxdigit(**cp) && (diff <= max_digit)) {
+ while (diff <= max_digit) {
+ int value = hex_to_bin(**cp);
- if (isdigit(**cp))
- value = **cp - '0';
- else
- value = tolower(**cp) - 'a' + 10;
+ if (value < 0)
+ break;
*val = *val * 16 + value;
(*cp)++;
diff++;
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 6c9fa15aac7b..2d32233943a9 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/cio/chp.c
*
- * Copyright IBM Corp. 1999,2007
+ * Copyright IBM Corp. 1999,2010
* Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
@@ -54,12 +54,6 @@ static struct work_struct cfg_work;
/* Wait queue for configure completion events. */
static wait_queue_head_t cfg_wait_queue;
-/* Return channel_path struct for given chpid. */
-static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
-{
- return channel_subsystems[chpid.cssid]->chps[chpid.id];
-}
-
/* Set vary state for given chpid. */
static void set_chp_logically_online(struct chp_id chpid, int onoff)
{
@@ -241,11 +235,13 @@ static ssize_t chp_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct channel_path *chp = to_channelpath(dev);
+ int status;
- if (!chp)
- return 0;
- return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") :
- sprintf(buf, "offline\n"));
+ mutex_lock(&chp->lock);
+ status = chp->state;
+ mutex_unlock(&chp->lock);
+
+ return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n");
}
static ssize_t chp_status_write(struct device *dev,
@@ -261,15 +257,18 @@ static ssize_t chp_status_write(struct device *dev,
if (!num_args)
return count;
- if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1"))
+ if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) {
+ mutex_lock(&cp->lock);
error = s390_vary_chpid(cp->chpid, 1);
- else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0"))
+ mutex_unlock(&cp->lock);
+ } else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) {
+ mutex_lock(&cp->lock);
error = s390_vary_chpid(cp->chpid, 0);
- else
+ mutex_unlock(&cp->lock);
+ } else
error = -EINVAL;
return error < 0 ? error : count;
-
}
static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
@@ -315,10 +314,12 @@ static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct channel_path *chp = to_channelpath(dev);
+ u8 type;
- if (!chp)
- return 0;
- return sprintf(buf, "%x\n", chp->desc.desc);
+ mutex_lock(&chp->lock);
+ type = chp->desc.desc;
+ mutex_unlock(&chp->lock);
+ return sprintf(buf, "%x\n", type);
}
static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
@@ -395,6 +396,7 @@ int chp_new(struct chp_id chpid)
chp->state = 1;
chp->dev.parent = &channel_subsystems[chpid.cssid]->device;
chp->dev.release = chp_release;
+ mutex_init(&chp->lock);
/* Obtain channel path description and fill it in. */
ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc);
@@ -464,7 +466,10 @@ void *chp_get_chp_desc(struct chp_id chpid)
desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
if (!desc)
return NULL;
+
+ mutex_lock(&chp->lock);
memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+ mutex_unlock(&chp->lock);
return desc;
}
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 26c3d2246176..12b4903d6fe3 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -1,7 +1,7 @@
/*
* drivers/s390/cio/chp.h
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007,2010
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
@@ -10,6 +10,7 @@
#include <linux/types.h>
#include <linux/device.h>
+#include <linux/mutex.h>
#include <asm/chpid.h>
#include "chsc.h"
#include "css.h"
@@ -40,16 +41,23 @@ static inline int chp_test_bit(u8 *bitmap, int num)
struct channel_path {
+ struct device dev;
struct chp_id chpid;
+ struct mutex lock; /* Serialize access to below members. */
int state;
struct channel_path_desc desc;
/* Channel-measurement related stuff: */
int cmg;
int shared;
void *cmg_chars;
- struct device dev;
};
+/* Return channel_path struct for given chpid. */
+static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
+{
+ return channel_subsystems[chpid.cssid]->chps[chpid.id];
+}
+
int chp_get_status(struct chp_id chpid);
u8 chp_get_sch_opm(struct subchannel *sch);
int chp_is_registered(struct chp_id chpid);
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 4cbb1a6ca33c..1aaddea673e0 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -2,7 +2,7 @@
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
*
- * Copyright IBM Corp. 1999,2008
+ * Copyright IBM Corp. 1999,2010
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
@@ -29,8 +29,8 @@
#include "chsc.h"
static void *sei_page;
-static DEFINE_SPINLOCK(siosl_lock);
-static DEFINE_SPINLOCK(sda_lock);
+static void *chsc_page;
+static DEFINE_SPINLOCK(chsc_page_lock);
/**
* chsc_error_from_response() - convert a chsc response to an error
@@ -85,17 +85,15 @@ struct chsc_ssd_area {
int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
{
- unsigned long page;
struct chsc_ssd_area *ssd_area;
int ccode;
int ret;
int i;
int mask;
- page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!page)
- return -ENOMEM;
- ssd_area = (struct chsc_ssd_area *) page;
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ ssd_area = chsc_page;
ssd_area->request.length = 0x0010;
ssd_area->request.code = 0x0004;
ssd_area->ssid = schid.ssid;
@@ -106,25 +104,25 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
/* Check response. */
if (ccode > 0) {
ret = (ccode == 3) ? -ENODEV : -EBUSY;
- goto out_free;
+ goto out;
}
ret = chsc_error_from_response(ssd_area->response.code);
if (ret != 0) {
CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
schid.ssid, schid.sch_no,
ssd_area->response.code);
- goto out_free;
+ goto out;
}
if (!ssd_area->sch_valid) {
ret = -ENODEV;
- goto out_free;
+ goto out;
}
/* Copy data */
ret = 0;
memset(ssd, 0, sizeof(struct chsc_ssd_info));
if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
(ssd_area->st != SUBCHANNEL_TYPE_MSG))
- goto out_free;
+ goto out;
ssd->path_mask = ssd_area->path_mask;
ssd->fla_valid_mask = ssd_area->fla_valid_mask;
for (i = 0; i < 8; i++) {
@@ -136,8 +134,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
if (ssd_area->fla_valid_mask & mask)
ssd->fla[i] = ssd_area->fla[i];
}
-out_free:
- free_page(page);
+out:
+ spin_unlock_irq(&chsc_page_lock);
return ret;
}
@@ -497,6 +495,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
*/
int chsc_chp_vary(struct chp_id chpid, int on)
{
+ struct channel_path *chp = chpid_to_chp(chpid);
struct chp_link link;
memset(&link, 0, sizeof(struct chp_link));
@@ -506,11 +505,12 @@ int chsc_chp_vary(struct chp_id chpid, int on)
/*
* Redo PathVerification on the devices the chpid connects to
*/
-
- if (on)
+ if (on) {
+ /* Try to update the channel path descritor. */
+ chsc_determine_base_channel_path_desc(chpid, &chp->desc);
for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
__s390_vary_chpid_on, &link);
- else
+ } else
for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
NULL, &link);
@@ -552,7 +552,7 @@ cleanup:
return ret;
}
-int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
+int __chsc_do_secm(struct channel_subsystem *css, int enable)
{
struct {
struct chsc_header request;
@@ -573,7 +573,9 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
} __attribute__ ((packed)) *secm_area;
int ret, ccode;
- secm_area = page;
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ secm_area = chsc_page;
secm_area->request.length = 0x0050;
secm_area->request.code = 0x0016;
@@ -584,8 +586,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
secm_area->operation_code = enable ? 0 : 1;
ccode = chsc(secm_area);
- if (ccode > 0)
- return (ccode == 3) ? -ENODEV : -EBUSY;
+ if (ccode > 0) {
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
+ goto out;
+ }
switch (secm_area->response.code) {
case 0x0102:
@@ -598,37 +602,32 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
if (ret != 0)
CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
secm_area->response.code);
+out:
+ spin_unlock_irq(&chsc_page_lock);
return ret;
}
int
chsc_secm(struct channel_subsystem *css, int enable)
{
- void *secm_area;
int ret;
- secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!secm_area)
- return -ENOMEM;
-
if (enable && !css->cm_enabled) {
css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!css->cub_addr1 || !css->cub_addr2) {
free_page((unsigned long)css->cub_addr1);
free_page((unsigned long)css->cub_addr2);
- free_page((unsigned long)secm_area);
return -ENOMEM;
}
}
- ret = __chsc_do_secm(css, enable, secm_area);
+ ret = __chsc_do_secm(css, enable);
if (!ret) {
css->cm_enabled = enable;
if (css->cm_enabled) {
ret = chsc_add_cmg_attr(css);
if (ret) {
- memset(secm_area, 0, PAGE_SIZE);
- __chsc_do_secm(css, 0, secm_area);
+ __chsc_do_secm(css, 0);
css->cm_enabled = 0;
}
} else
@@ -638,44 +637,24 @@ chsc_secm(struct channel_subsystem *css, int enable)
free_page((unsigned long)css->cub_addr1);
free_page((unsigned long)css->cub_addr2);
}
- free_page((unsigned long)secm_area);
return ret;
}
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
- int c, int m,
- struct chsc_response_struct *resp)
+ int c, int m, void *page)
{
+ struct chsc_scpd *scpd_area;
int ccode, ret;
- struct {
- struct chsc_header request;
- u32 : 2;
- u32 m : 1;
- u32 c : 1;
- u32 fmt : 4;
- u32 cssid : 8;
- u32 : 4;
- u32 rfmt : 4;
- u32 first_chpid : 8;
- u32 : 24;
- u32 last_chpid : 8;
- u32 zeroes1;
- struct chsc_header response;
- u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *scpd_area;
-
if ((rfmt == 1) && !css_general_characteristics.fcs)
return -EINVAL;
if ((rfmt == 2) && !css_general_characteristics.cib)
return -EINVAL;
- scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!scpd_area)
- return -ENOMEM;
+ memset(page, 0, PAGE_SIZE);
+ scpd_area = page;
scpd_area->request.length = 0x0010;
scpd_area->request.code = 0x0002;
-
scpd_area->cssid = chpid.cssid;
scpd_area->first_chpid = chpid.id;
scpd_area->last_chpid = chpid.id;
@@ -685,20 +664,13 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
scpd_area->rfmt = rfmt;
ccode = chsc(scpd_area);
- if (ccode > 0) {
- ret = (ccode == 3) ? -ENODEV : -EBUSY;
- goto out;
- }
+ if (ccode > 0)
+ return (ccode == 3) ? -ENODEV : -EBUSY;
ret = chsc_error_from_response(scpd_area->response.code);
- if (ret == 0)
- /* Success. */
- memcpy(resp, &scpd_area->response, scpd_area->response.length);
- else
+ if (ret)
CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
scpd_area->response.code);
-out:
- free_page((unsigned long)scpd_area);
return ret;
}
EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
@@ -707,17 +679,19 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid,
struct channel_path_desc *desc)
{
struct chsc_response_struct *chsc_resp;
+ struct chsc_scpd *scpd_area;
+ unsigned long flags;
int ret;
- chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL);
- if (!chsc_resp)
- return -ENOMEM;
- ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp);
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ scpd_area = chsc_page;
+ ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area);
if (ret)
- goto out_free;
+ goto out;
+ chsc_resp = (void *)&scpd_area->response;
memcpy(desc, &chsc_resp->data, sizeof(*desc));
-out_free:
- kfree(chsc_resp);
+out:
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -725,33 +699,22 @@ static void
chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
struct cmg_chars *chars)
{
- switch (chp->cmg) {
- case 2:
- case 3:
- chp->cmg_chars = kmalloc(sizeof(struct cmg_chars),
- GFP_KERNEL);
- if (chp->cmg_chars) {
- int i, mask;
- struct cmg_chars *cmg_chars;
-
- cmg_chars = chp->cmg_chars;
- for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
- mask = 0x80 >> (i + 3);
- if (cmcv & mask)
- cmg_chars->values[i] = chars->values[i];
- else
- cmg_chars->values[i] = 0;
- }
- }
- break;
- default:
- /* No cmg-dependent data. */
- break;
+ struct cmg_chars *cmg_chars;
+ int i, mask;
+
+ cmg_chars = chp->cmg_chars;
+ for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
+ mask = 0x80 >> (i + 3);
+ if (cmcv & mask)
+ cmg_chars->values[i] = chars->values[i];
+ else
+ cmg_chars->values[i] = 0;
}
}
int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
+ struct cmg_chars *cmg_chars;
int ccode, ret;
struct {
@@ -775,13 +738,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
u32 data[NR_MEASUREMENT_CHARS];
} __attribute__ ((packed)) *scmc_area;
- scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!scmc_area)
+ chp->cmg_chars = NULL;
+ cmg_chars = kmalloc(sizeof(*cmg_chars), GFP_KERNEL);
+ if (!cmg_chars)
return -ENOMEM;
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ scmc_area = chsc_page;
scmc_area->request.length = 0x0010;
scmc_area->request.code = 0x0022;
-
scmc_area->first_chpid = chp->chpid.id;
scmc_area->last_chpid = chp->chpid.id;
@@ -792,53 +758,65 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
}
ret = chsc_error_from_response(scmc_area->response.code);
- if (ret == 0) {
- /* Success. */
- if (!scmc_area->not_valid) {
- chp->cmg = scmc_area->cmg;
- chp->shared = scmc_area->shared;
- chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
- (struct cmg_chars *)
- &scmc_area->data);
- } else {
- chp->cmg = -1;
- chp->shared = -1;
- }
- } else {
+ if (ret) {
CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n",
scmc_area->response.code);
+ goto out;
+ }
+ if (scmc_area->not_valid) {
+ chp->cmg = -1;
+ chp->shared = -1;
+ goto out;
}
+ chp->cmg = scmc_area->cmg;
+ chp->shared = scmc_area->shared;
+ if (chp->cmg != 2 && chp->cmg != 3) {
+ /* No cmg-dependent data. */
+ goto out;
+ }
+ chp->cmg_chars = cmg_chars;
+ chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
+ (struct cmg_chars *) &scmc_area->data);
out:
- free_page((unsigned long)scmc_area);
+ spin_unlock_irq(&chsc_page_lock);
+ if (!chp->cmg_chars)
+ kfree(cmg_chars);
+
return ret;
}
-int __init chsc_alloc_sei_area(void)
+int __init chsc_init(void)
{
int ret;
sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!sei_page) {
- CIO_MSG_EVENT(0, "Can't allocate page for processing of "
- "chsc machine checks!\n");
- return -ENOMEM;
+ chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sei_page || !chsc_page) {
+ ret = -ENOMEM;
+ goto out_err;
}
ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw);
if (ret)
- kfree(sei_page);
+ goto out_err;
+ return ret;
+out_err:
+ free_page((unsigned long)chsc_page);
+ free_page((unsigned long)sei_page);
return ret;
}
-void __init chsc_free_sei_area(void)
+void __init chsc_init_cleanup(void)
{
crw_unregister_handler(CRW_RSC_CSS);
- kfree(sei_page);
+ free_page((unsigned long)chsc_page);
+ free_page((unsigned long)sei_page);
}
int chsc_enable_facility(int operation_code)
{
+ unsigned long flags;
int ret;
- static struct {
+ struct {
struct chsc_header request;
u8 reserved1:4;
u8 format:4;
@@ -851,32 +829,33 @@ int chsc_enable_facility(int operation_code)
u32 reserved5:4;
u32 format2:4;
u32 reserved6:24;
- } __attribute__ ((packed, aligned(4096))) sda_area;
+ } __attribute__ ((packed)) *sda_area;
- spin_lock(&sda_lock);
- memset(&sda_area, 0, sizeof(sda_area));
- sda_area.request.length = 0x0400;
- sda_area.request.code = 0x0031;
- sda_area.operation_code = operation_code;
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ memset(chsc_page, 0, PAGE_SIZE);
+ sda_area = chsc_page;
+ sda_area->request.length = 0x0400;
+ sda_area->request.code = 0x0031;
+ sda_area->operation_code = operation_code;
- ret = chsc(&sda_area);
+ ret = chsc(sda_area);
if (ret > 0) {
ret = (ret == 3) ? -ENODEV : -EBUSY;
goto out;
}
- switch (sda_area.response.code) {
+ switch (sda_area->response.code) {
case 0x0101:
ret = -EOPNOTSUPP;
break;
default:
- ret = chsc_error_from_response(sda_area.response.code);
+ ret = chsc_error_from_response(sda_area->response.code);
}
if (ret != 0)
CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
- operation_code, sda_area.response.code);
- out:
- spin_unlock(&sda_lock);
+ operation_code, sda_area->response.code);
+out:
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -895,13 +874,12 @@ chsc_determine_css_characteristics(void)
struct chsc_header response;
u32 reserved4;
u32 general_char[510];
- u32 chsc_char[518];
+ u32 chsc_char[508];
} __attribute__ ((packed)) *scsc_area;
- scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!scsc_area)
- return -ENOMEM;
-
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ scsc_area = chsc_page;
scsc_area->request.length = 0x0010;
scsc_area->request.code = 0x0010;
@@ -921,7 +899,7 @@ chsc_determine_css_characteristics(void)
CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
scsc_area->response.code);
exit:
- free_page ((unsigned long) scsc_area);
+ spin_unlock_irq(&chsc_page_lock);
return result;
}
@@ -976,29 +954,29 @@ int chsc_sstpi(void *page, void *result, size_t size)
return (rr->response.code == 0x0001) ? 0 : -EIO;
}
-static struct {
- struct chsc_header request;
- u32 word1;
- struct subchannel_id sid;
- u32 word3;
- struct chsc_header response;
- u32 word[11];
-} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE)));
-
int chsc_siosl(struct subchannel_id schid)
{
+ struct {
+ struct chsc_header request;
+ u32 word1;
+ struct subchannel_id sid;
+ u32 word3;
+ struct chsc_header response;
+ u32 word[11];
+ } __attribute__ ((packed)) *siosl_area;
unsigned long flags;
int ccode;
int rc;
- spin_lock_irqsave(&siosl_lock, flags);
- memset(&siosl_area, 0, sizeof(siosl_area));
- siosl_area.request.length = 0x0010;
- siosl_area.request.code = 0x0046;
- siosl_area.word1 = 0x80000000;
- siosl_area.sid = schid;
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ memset(chsc_page, 0, PAGE_SIZE);
+ siosl_area = chsc_page;
+ siosl_area->request.length = 0x0010;
+ siosl_area->request.code = 0x0046;
+ siosl_area->word1 = 0x80000000;
+ siosl_area->sid = schid;
- ccode = chsc(&siosl_area);
+ ccode = chsc(siosl_area);
if (ccode > 0) {
if (ccode == 3)
rc = -ENODEV;
@@ -1008,17 +986,16 @@ int chsc_siosl(struct subchannel_id schid)
schid.ssid, schid.sch_no, ccode);
goto out;
}
- rc = chsc_error_from_response(siosl_area.response.code);
+ rc = chsc_error_from_response(siosl_area->response.code);
if (rc)
CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n",
schid.ssid, schid.sch_no,
- siosl_area.response.code);
+ siosl_area->response.code);
else
CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n",
schid.ssid, schid.sch_no);
out:
- spin_unlock_irqrestore(&siosl_lock, flags);
-
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(chsc_siosl);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 5453013f094b..6693f5e3176f 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -57,21 +57,39 @@ struct chsc_ssd_info {
struct chp_id chpid[8];
u16 fla[8];
};
+
+struct chsc_scpd {
+ struct chsc_header request;
+ u32:2;
+ u32 m:1;
+ u32 c:1;
+ u32 fmt:4;
+ u32 cssid:8;
+ u32:4;
+ u32 rfmt:4;
+ u32 first_chpid:8;
+ u32:24;
+ u32 last_chpid:8;
+ u32 zeroes1;
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 20];
+} __attribute__ ((packed));
+
+
extern int chsc_get_ssd_info(struct subchannel_id schid,
struct chsc_ssd_info *ssd);
extern int chsc_determine_css_characteristics(void);
-extern int chsc_alloc_sei_area(void);
-extern void chsc_free_sei_area(void);
+extern int chsc_init(void);
+extern void chsc_init_cleanup(void);
extern int chsc_enable_facility(int);
struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
-int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page);
+int __chsc_do_secm(struct channel_subsystem *css, int enable);
int chsc_chp_vary(struct chp_id chpid, int on);
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
- int c, int m,
- struct chsc_response_struct *resp);
+ int c, int m, void *page);
int chsc_determine_base_channel_path_desc(struct chp_id chpid,
struct channel_path_desc *desc);
void chsc_chp_online(struct chp_id chpid);
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index f2b77e7bfc6f..3c3f3ffe2179 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -688,25 +688,31 @@ out_free:
static int chsc_ioctl_chpd(void __user *user_chpd)
{
+ struct chsc_scpd *scpd_area;
struct chsc_cpd_info *chpd;
int ret;
chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
- if (!chpd)
- return -ENOMEM;
+ scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!scpd_area || !chpd) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
ret = -EFAULT;
goto out_free;
}
ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
chpd->rfmt, chpd->c, chpd->m,
- &chpd->chpdb);
+ scpd_area);
if (ret)
goto out_free;
+ memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length);
if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
ret = -EFAULT;
out_free:
kfree(chpd);
+ free_page((unsigned long)scpd_area);
return ret;
}
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index ca8e1c240c3c..a5050e217150 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -1,7 +1,7 @@
/*
* driver for channel subsystem
*
- * Copyright IBM Corp. 2002, 2009
+ * Copyright IBM Corp. 2002, 2010
*
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
@@ -577,7 +577,7 @@ static int __unset_registered(struct device *dev, void *data)
return 0;
}
-void css_schedule_eval_all_unreg(void)
+static void css_schedule_eval_all_unreg(void)
{
unsigned long flags;
struct idset *unreg_set;
@@ -790,7 +790,6 @@ static struct notifier_block css_reboot_notifier = {
static int css_power_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
- void *secm_area;
int ret, i;
switch (event) {
@@ -806,15 +805,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event,
mutex_unlock(&css->mutex);
continue;
}
- secm_area = (void *)get_zeroed_page(GFP_KERNEL |
- GFP_DMA);
- if (secm_area) {
- if (__chsc_do_secm(css, 0, secm_area))
- ret = NOTIFY_BAD;
- free_page((unsigned long)secm_area);
- } else
+ if (__chsc_do_secm(css, 0))
ret = NOTIFY_BAD;
-
mutex_unlock(&css->mutex);
}
break;
@@ -830,15 +822,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event,
mutex_unlock(&css->mutex);
continue;
}
- secm_area = (void *)get_zeroed_page(GFP_KERNEL |
- GFP_DMA);
- if (secm_area) {
- if (__chsc_do_secm(css, 1, secm_area))
- ret = NOTIFY_BAD;
- free_page((unsigned long)secm_area);
- } else
+ if (__chsc_do_secm(css, 1))
ret = NOTIFY_BAD;
-
mutex_unlock(&css->mutex);
}
/* search for subchannels, which appeared during hibernation */
@@ -863,14 +848,11 @@ static int __init css_bus_init(void)
{
int ret, i;
- ret = chsc_determine_css_characteristics();
- if (ret == -ENOMEM)
- goto out;
-
- ret = chsc_alloc_sei_area();
+ ret = chsc_init();
if (ret)
- goto out;
+ return ret;
+ chsc_determine_css_characteristics();
/* Try to enable MSS. */
ret = chsc_enable_facility(CHSC_SDA_OC_MSS);
if (ret)
@@ -956,9 +938,9 @@ out_unregister:
}
bus_unregister(&css_bus_type);
out:
- crw_unregister_handler(CRW_RSC_CSS);
- chsc_free_sei_area();
+ crw_unregister_handler(CRW_RSC_SCH);
idset_free(slow_subchannel_set);
+ chsc_init_cleanup();
pr_alert("The CSS device driver initialization failed with "
"errno=%d\n", ret);
return ret;
@@ -978,9 +960,9 @@ static void __init css_bus_cleanup(void)
device_unregister(&css->device);
}
bus_unregister(&css_bus_type);
- crw_unregister_handler(CRW_RSC_CSS);
- chsc_free_sei_area();
+ crw_unregister_handler(CRW_RSC_SCH);
idset_free(slow_subchannel_set);
+ chsc_init_cleanup();
isc_unregister(IO_SCH_ISC);
}
@@ -1048,7 +1030,16 @@ subsys_initcall_sync(channel_subsystem_init_sync);
void channel_subsystem_reinit(void)
{
+ struct channel_path *chp;
+ struct chp_id chpid;
+
chsc_enable_facility(CHSC_SDA_OC_MSS);
+ chp_id_for_each(&chpid) {
+ chp = chpid_to_chp(chpid);
+ if (!chp)
+ continue;
+ chsc_determine_base_channel_path_desc(chpid, &chp->desc);
+ }
}
#ifdef CONFIG_PROC_FS
@@ -1200,6 +1191,7 @@ static int css_pm_restore(struct device *dev)
struct subchannel *sch = to_subchannel(dev);
struct css_driver *drv;
+ css_update_ssd_info(sch);
if (!sch->dev.driver)
return 0;
drv = to_cssdriver(sch->dev.driver);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 51bd3687d163..2ff8a22d4257 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1147,6 +1147,7 @@ err:
static int io_subchannel_chp_event(struct subchannel *sch,
struct chp_link *link, int event)
{
+ struct ccw_device *cdev = sch_get_cdev(sch);
int mask;
mask = chp_ssd_get_mask(&sch->ssd_info, link);
@@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch,
case CHP_VARY_OFF:
sch->opm &= ~mask;
sch->lpm &= ~mask;
+ if (cdev)
+ cdev->private->path_gone_mask |= mask;
io_subchannel_terminate_path(sch, mask);
break;
case CHP_VARY_ON:
sch->opm |= mask;
sch->lpm |= mask;
+ if (cdev)
+ cdev->private->path_new_mask |= mask;
io_subchannel_verify(sch);
break;
case CHP_OFFLINE:
if (cio_update_schib(sch))
return -ENODEV;
+ if (cdev)
+ cdev->private->path_gone_mask |= mask;
io_subchannel_terminate_path(sch, mask);
break;
case CHP_ONLINE:
if (cio_update_schib(sch))
return -ENODEV;
sch->lpm |= mask & sch->opm;
+ if (cdev)
+ cdev->private->path_new_mask |= mask;
io_subchannel_verify(sch);
break;
}
@@ -1196,6 +1205,7 @@ static void io_subchannel_quiesce(struct subchannel *sch)
cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
while (ret == -EBUSY) {
cdev->private->state = DEV_STATE_QUIESCE;
+ cdev->private->iretry = 255;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, HZ/10);
@@ -1468,9 +1478,13 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
goto out;
break;
case IO_SCH_UNREG_ATTACH:
+ if (cdev->private->flags.resuming) {
+ /* Device will be handled later. */
+ rc = 0;
+ goto out;
+ }
/* Unregister ccw device. */
- if (!cdev->private->flags.resuming)
- ccw_device_unregister(cdev);
+ ccw_device_unregister(cdev);
break;
default:
break;
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index c9b852647f01..a845695ac314 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -174,7 +174,10 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
ret = cio_clear (sch);
return (ret == 0) ? -EBUSY : ret;
}
- panic("Can't stop i/o on subchannel.\n");
+ /* Function was unsuccessful */
+ CIO_MSG_EVENT(0, "0.%x.%04x: could not stop I/O\n",
+ cdev->private->dev_id.ssid, cdev->private->dev_id.devno);
+ return -EIO;
}
void ccw_device_update_sense_data(struct ccw_device *cdev)
@@ -349,9 +352,13 @@ out:
static void ccw_device_oper_notify(struct ccw_device *cdev)
{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
/* Reenable channel measurements, if needed. */
ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
+ /* Save indication for new paths. */
+ cdev->private->path_new_mask = sch->vpm;
return;
}
/* Driver doesn't want device back. */
@@ -462,6 +469,32 @@ static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
}
}
+static void ccw_device_report_path_events(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int path_event[8];
+ int chp, mask;
+
+ for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) {
+ path_event[chp] = PE_NONE;
+ if (mask & cdev->private->path_gone_mask & ~(sch->vpm))
+ path_event[chp] |= PE_PATH_GONE;
+ if (mask & cdev->private->path_new_mask & sch->vpm)
+ path_event[chp] |= PE_PATH_AVAILABLE;
+ if (mask & cdev->private->pgid_reset_mask & sch->vpm)
+ path_event[chp] |= PE_PATHGROUP_ESTABLISHED;
+ }
+ if (cdev->online && cdev->drv->path_event)
+ cdev->drv->path_event(cdev, path_event);
+}
+
+static void ccw_device_reset_path_events(struct ccw_device *cdev)
+{
+ cdev->private->path_gone_mask = 0;
+ cdev->private->path_new_mask = 0;
+ cdev->private->pgid_reset_mask = 0;
+}
+
void
ccw_device_verify_done(struct ccw_device *cdev, int err)
{
@@ -498,6 +531,7 @@ callback:
&cdev->private->irb);
memset(&cdev->private->irb, 0, sizeof(struct irb));
}
+ ccw_device_report_path_events(cdev);
break;
case -ETIME:
case -EUSERS:
@@ -516,6 +550,7 @@ callback:
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break;
}
+ ccw_device_reset_path_events(cdev);
}
/*
@@ -734,13 +769,14 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
ccw_device_set_timeout(cdev, 0);
+ cdev->private->iretry = 255;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
cdev->private->state = DEV_STATE_TIMEOUT_KILL;
return;
}
- if (ret == -ENODEV)
+ if (ret)
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
else if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
@@ -837,6 +873,7 @@ void ccw_device_kill_io(struct ccw_device *cdev)
{
int ret;
+ cdev->private->iretry = 255;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 82a5ad0d63f6..07a4fd29f096 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev)
spid_do(cdev);
}
+static int pgid_is_reset(struct pgid *p)
+{
+ char *c;
+
+ for (c = (char *)p + 1; c < (char *)(p + 1); c++) {
+ if (*c != 0)
+ return 0;
+ }
+ return 1;
+}
+
static int pgid_cmp(struct pgid *p1, struct pgid *p2)
{
return memcmp((char *) p1 + 1, (char *) p2 + 1,
@@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2)
* Determine pathgroup state from PGID data.
*/
static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
- int *mismatch, int *reserved, int *reset)
+ int *mismatch, int *reserved, u8 *reset)
{
struct pgid *pgid = &cdev->private->pgid[0];
struct pgid *first = NULL;
@@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
continue;
if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
*reserved = 1;
- if (pgid->inf.ps.state1 == SNID_STATE1_RESET) {
- /* A PGID was reset. */
- *reset = 1;
+ if (pgid_is_reset(pgid)) {
+ *reset |= lpm;
continue;
}
if (!first) {
@@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
struct pgid *pgid;
int mismatch = 0;
int reserved = 0;
- int reset = 0;
+ u8 reset = 0;
u8 donepm;
if (rc)
@@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc)
donepm = pgid_to_donepm(cdev);
sch->vpm = donepm & sch->opm;
cdev->private->pgid_todo_mask &= ~donepm;
+ cdev->private->pgid_reset_mask |= reset;
pgid_fill(cdev, pgid);
}
out:
CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
- "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid,
+ "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid,
id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
cdev->private->pgid_todo_mask, mismatch, reserved, reset);
switch (rc) {
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 469ef93f2302..d024d2c21897 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -151,8 +151,11 @@ struct ccw_device_private {
struct subchannel_id schid; /* subchannel number */
struct ccw_request req; /* internal I/O request */
int iretry;
- u8 pgid_valid_mask; /* mask of valid PGIDs */
- u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */
+ u8 pgid_valid_mask; /* mask of valid PGIDs */
+ u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */
+ u8 pgid_reset_mask; /* mask of PGIDs which were reset */
+ u8 path_gone_mask; /* mask of paths, that became unavailable */
+ u8 path_new_mask; /* mask of paths, that became available */
struct {
unsigned int fast:1; /* post with "channel end" */
unsigned int repall:1; /* report every interrupt status */
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 91c6028d7b74..8fd8c62455e9 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -154,14 +154,7 @@ static inline int ap_instructions_available(void)
*/
static int ap_interrupts_available(void)
{
- unsigned long long facility_bits[2];
-
- if (stfle(facility_bits, 2) <= 1)
- return 0;
- if (!(facility_bits[0] & (1ULL << 61)) ||
- !(facility_bits[1] & (1ULL << 62)))
- return 0;
- return 1;
+ return test_facility(1) && test_facility(2);
}
/**
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 5a46b8c5d68a..375aeeaf9ea5 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -372,21 +372,22 @@ static void hotplug_devices(struct work_struct *dummy)
/*
* we emulate the request_irq behaviour on top of s390 extints
*/
-static void kvm_extint_handler(u16 code)
+static void kvm_extint_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
struct virtqueue *vq;
u16 subcode;
u32 param;
- subcode = S390_lowcore.cpu_addr;
+ subcode = ext_int_code >> 16;
if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
return;
/* The LSB might be overloaded, we have to mask it */
- vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL);
+ vq = (struct virtqueue *)(param64 & ~1UL);
/* We use ext_params to decide what this interrupt means */
- param = S390_lowcore.ext_params & VIRTIO_PARAM_MASK;
+ param = param32 & VIRTIO_PARAM_MASK;
switch (param) {
case VIRTIO_PARAM_CONFIG_CHANGED:
diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c
index 13f48e28a1e1..a624f5af4320 100644
--- a/drivers/sbus/char/jsflash.c
+++ b/drivers/sbus/char/jsflash.c
@@ -461,7 +461,7 @@ static int jsflash_init(void)
{
int rc;
struct jsflash *jsf;
- int node;
+ phandle node;
char banner[128];
struct linux_prom_registers reg0;
diff --git a/drivers/serial/68328serial.h b/drivers/serial/68328serial.h
index 58aa2154655b..664ceb0a158c 100644
--- a/drivers/serial/68328serial.h
+++ b/drivers/serial/68328serial.h
@@ -181,13 +181,8 @@ struct m68k_serial {
/*
* Define the number of ports supported and their irqs.
*/
-#ifndef CONFIG_68328_SERIAL_UART2
#define NR_PORTS 1
#define UART_IRQ_DEFNS {UART_IRQ_NUM}
-#else
-#define NR_PORTS 2
-#define UART_IRQ_DEFNS {UART1_IRQ_NUM, UART2_IRQ_NUM}
-#endif
#endif /* __KERNEL__ */
#endif /* !(_MC683XX_SERIAL_H) */
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 927816484397..aff9dcd051c6 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1410,6 +1410,33 @@ config SERIAL_OF_PLATFORM
Currently, only 8250 compatible ports are supported, but
others can easily be added.
+config SERIAL_OMAP
+ tristate "OMAP serial port support"
+ depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4
+ select SERIAL_CORE
+ help
+ If you have a machine based on an Texas Instruments OMAP CPU you
+ can enable its onboard serial ports by enabling this option.
+
+ By enabling this option you take advantage of dma feature available
+ with the omap-serial driver. DMA support can be enabled from platform
+ data.
+
+config SERIAL_OMAP_CONSOLE
+ bool "Console on OMAP serial port"
+ depends on SERIAL_OMAP
+ select SERIAL_CORE_CONSOLE
+ help
+ Select this option if you would like to use omap serial port as
+ console.
+
+ Even if you say Y here, the currently visible virtual console
+ (/dev/tty0) will still be used as the system console by default, but
+ you can alter that using a kernel command line option such as
+ "console=ttyOx". (Try "man bootparam" or see the documentation of
+ your boot loader about how to pass options to the kernel at
+ boot time.)
+
config SERIAL_OF_PLATFORM_NWPSERIAL
tristate "NWP serial port driver"
depends on PPC_OF && PPC_DCR
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 1ca4fd599ffe..c5705765454f 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -88,3 +88,4 @@ obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
+obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c
index 2af8fd113123..17849dcb9adc 100644
--- a/drivers/serial/of_serial.c
+++ b/drivers/serial/of_serial.c
@@ -31,8 +31,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
{
struct resource resource;
struct device_node *np = ofdev->dev.of_node;
- const unsigned int *clk, *spd;
- const u32 *prop;
+ const __be32 *clk, *spd;
+ const __be32 *prop;
int ret, prop_size;
memset(port, 0, sizeof *port);
@@ -55,23 +55,23 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
/* Check for shifted address mapping */
prop = of_get_property(np, "reg-offset", &prop_size);
if (prop && (prop_size == sizeof(u32)))
- port->mapbase += *prop;
+ port->mapbase += be32_to_cpup(prop);
/* Check for registers offset within the devices address range */
prop = of_get_property(np, "reg-shift", &prop_size);
if (prop && (prop_size == sizeof(u32)))
- port->regshift = *prop;
+ port->regshift = be32_to_cpup(prop);
port->irq = irq_of_parse_and_map(np, 0);
port->iotype = UPIO_MEM;
port->type = type;
- port->uartclk = *clk;
+ port->uartclk = be32_to_cpup(clk);
port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP
| UPF_FIXED_PORT | UPF_FIXED_TYPE;
port->dev = &ofdev->dev;
/* If current-speed was set, then try not to change it. */
if (spd)
- port->custom_divisor = *clk / (16 * (*spd));
+ port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd)));
return 0;
}
diff --git a/drivers/serial/omap-serial.c b/drivers/serial/omap-serial.c
new file mode 100644
index 000000000000..14365f72b664
--- /dev/null
+++ b/drivers/serial/omap-serial.c
@@ -0,0 +1,1333 @@
+/*
+ * Driver for OMAP-UART controller.
+ * Based on drivers/serial/8250.c
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * Authors:
+ * Govindraj R <govindraj.raja@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Note: This driver is made seperate from 8250 driver as we cannot
+ * over load 8250 driver with omap platform specific configuration for
+ * features like DMA, it makes easier to implement features like DMA and
+ * hardware flow control and software flow control configuration with
+ * this driver as required for the omap-platform.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/serial_core.h>
+#include <linux/irq.h>
+
+#include <plat/dma.h>
+#include <plat/dmtimer.h>
+#include <plat/omap-serial.h>
+
+static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
+
+/* Forward declaration of functions */
+static void uart_tx_dma_callback(int lch, u16 ch_status, void *data);
+static void serial_omap_rx_timeout(unsigned long uart_no);
+static int serial_omap_start_rxdma(struct uart_omap_port *up);
+
+static inline unsigned int serial_in(struct uart_omap_port *up, int offset)
+{
+ offset <<= up->port.regshift;
+ return readw(up->port.membase + offset);
+}
+
+static inline void serial_out(struct uart_omap_port *up, int offset, int value)
+{
+ offset <<= up->port.regshift;
+ writew(value, up->port.membase + offset);
+}
+
+static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
+{
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ serial_out(up, UART_FCR, 0);
+}
+
+/*
+ * serial_omap_get_divisor - calculate divisor value
+ * @port: uart port info
+ * @baud: baudrate for which divisor needs to be calculated.
+ *
+ * We have written our own function to get the divisor so as to support
+ * 13x mode. 3Mbps Baudrate as an different divisor.
+ * Reference OMAP TRM Chapter 17:
+ * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates
+ * referring to oversampling - divisor value
+ * baudrate 460,800 to 3,686,400 all have divisor 13
+ * except 3,000,000 which has divisor value 16
+ */
+static unsigned int
+serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
+{
+ unsigned int divisor;
+
+ if (baud > OMAP_MODE13X_SPEED && baud != 3000000)
+ divisor = 13;
+ else
+ divisor = 16;
+ return port->uartclk/(baud * divisor);
+}
+
+static void serial_omap_stop_rxdma(struct uart_omap_port *up)
+{
+ if (up->uart_dma.rx_dma_used) {
+ del_timer(&up->uart_dma.rx_timer);
+ omap_stop_dma(up->uart_dma.rx_dma_channel);
+ omap_free_dma(up->uart_dma.rx_dma_channel);
+ up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
+ up->uart_dma.rx_dma_used = false;
+ }
+}
+
+static void serial_omap_enable_ms(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+ dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->pdev->id);
+ up->ier |= UART_IER_MSI;
+ serial_out(up, UART_IER, up->ier);
+}
+
+static void serial_omap_stop_tx(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+ if (up->use_dma &&
+ up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) {
+ /*
+ * Check if dma is still active. If yes do nothing,
+ * return. Else stop dma
+ */
+ if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel))
+ return;
+ omap_stop_dma(up->uart_dma.tx_dma_channel);
+ omap_free_dma(up->uart_dma.tx_dma_channel);
+ up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
+ }
+
+ if (up->ier & UART_IER_THRI) {
+ up->ier &= ~UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+static void serial_omap_stop_rx(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+ if (up->use_dma)
+ serial_omap_stop_rxdma(up);
+ up->ier &= ~UART_IER_RLSI;
+ up->port.read_status_mask &= ~UART_LSR_DR;
+ serial_out(up, UART_IER, up->ier);
+}
+
+static inline void receive_chars(struct uart_omap_port *up, int *status)
+{
+ struct tty_struct *tty = up->port.state->port.tty;
+ unsigned int flag;
+ unsigned char ch, lsr = *status;
+ int max_count = 256;
+
+ do {
+ if (likely(lsr & UART_LSR_DR))
+ ch = serial_in(up, UART_RX);
+ flag = TTY_NORMAL;
+ up->port.icount.rx++;
+
+ if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
+ /*
+ * For statistics only
+ */
+ if (lsr & UART_LSR_BI) {
+ lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+ up->port.icount.brk++;
+ /*
+ * We do the SysRQ and SAK checking
+ * here because otherwise the break
+ * may get masked by ignore_status_mask
+ * or read_status_mask.
+ */
+ if (uart_handle_break(&up->port))
+ goto ignore_char;
+ } else if (lsr & UART_LSR_PE) {
+ up->port.icount.parity++;
+ } else if (lsr & UART_LSR_FE) {
+ up->port.icount.frame++;
+ }
+
+ if (lsr & UART_LSR_OE)
+ up->port.icount.overrun++;
+
+ /*
+ * Mask off conditions which should be ignored.
+ */
+ lsr &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_OMAP_CONSOLE
+ if (up->port.line == up->port.cons->index) {
+ /* Recover the break flag from console xmit */
+ lsr |= up->lsr_break_flag;
+ up->lsr_break_flag = 0;
+ }
+#endif
+ if (lsr & UART_LSR_BI)
+ flag = TTY_BREAK;
+ else if (lsr & UART_LSR_PE)
+ flag = TTY_PARITY;
+ else if (lsr & UART_LSR_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(&up->port, ch))
+ goto ignore_char;
+ uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
+ignore_char:
+ lsr = serial_in(up, UART_LSR);
+ } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
+ spin_unlock(&up->port.lock);
+ tty_flip_buffer_push(tty);
+ spin_lock(&up->port.lock);
+}
+
+static void transmit_chars(struct uart_omap_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ int count;
+
+ if (up->port.x_char) {
+ serial_out(up, UART_TX, up->port.x_char);
+ up->port.icount.tx++;
+ up->port.x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+ serial_omap_stop_tx(&up->port);
+ return;
+ }
+ count = up->port.fifosize / 4;
+ do {
+ serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ up->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (uart_circ_empty(xmit))
+ serial_omap_stop_tx(&up->port);
+}
+
+static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
+{
+ if (!(up->ier & UART_IER_THRI)) {
+ up->ier |= UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+static void serial_omap_start_tx(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct circ_buf *xmit;
+ unsigned int start;
+ int ret = 0;
+
+ if (!up->use_dma) {
+ serial_omap_enable_ier_thri(up);
+ return;
+ }
+
+ if (up->uart_dma.tx_dma_used)
+ return;
+
+ xmit = &up->port.state->xmit;
+
+ if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) {
+ ret = omap_request_dma(up->uart_dma.uart_dma_tx,
+ "UART Tx DMA",
+ (void *)uart_tx_dma_callback, up,
+ &(up->uart_dma.tx_dma_channel));
+
+ if (ret < 0) {
+ serial_omap_enable_ier_thri(up);
+ return;
+ }
+ }
+ spin_lock(&(up->uart_dma.tx_lock));
+ up->uart_dma.tx_dma_used = true;
+ spin_unlock(&(up->uart_dma.tx_lock));
+
+ start = up->uart_dma.tx_buf_dma_phys +
+ (xmit->tail & (UART_XMIT_SIZE - 1));
+
+ up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit);
+ /*
+ * It is a circular buffer. See if the buffer has wounded back.
+ * If yes it will have to be transferred in two separate dma
+ * transfers
+ */
+ if (start + up->uart_dma.tx_buf_size >=
+ up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE)
+ up->uart_dma.tx_buf_size =
+ (up->uart_dma.tx_buf_dma_phys +
+ UART_XMIT_SIZE) - start;
+
+ omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0,
+ OMAP_DMA_AMODE_CONSTANT,
+ up->uart_dma.uart_base, 0, 0);
+ omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0,
+ OMAP_DMA_AMODE_POST_INC, start, 0, 0);
+ omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel,
+ OMAP_DMA_DATA_TYPE_S8,
+ up->uart_dma.tx_buf_size, 1,
+ OMAP_DMA_SYNC_ELEMENT,
+ up->uart_dma.uart_dma_tx, 0);
+ /* FIXME: Cache maintenance needed here? */
+ omap_start_dma(up->uart_dma.tx_dma_channel);
+}
+
+static unsigned int check_modem_status(struct uart_omap_port *up)
+{
+ unsigned int status;
+
+ status = serial_in(up, UART_MSR);
+ status |= up->msr_saved_flags;
+ up->msr_saved_flags = 0;
+ if ((status & UART_MSR_ANY_DELTA) == 0)
+ return status;
+
+ if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
+ up->port.state != NULL) {
+ if (status & UART_MSR_TERI)
+ up->port.icount.rng++;
+ if (status & UART_MSR_DDSR)
+ up->port.icount.dsr++;
+ if (status & UART_MSR_DDCD)
+ uart_handle_dcd_change
+ (&up->port, status & UART_MSR_DCD);
+ if (status & UART_MSR_DCTS)
+ uart_handle_cts_change
+ (&up->port, status & UART_MSR_CTS);
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+ }
+
+ return status;
+}
+
+/**
+ * serial_omap_irq() - This handles the interrupt from one port
+ * @irq: uart port irq number
+ * @dev_id: uart port info
+ */
+static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
+{
+ struct uart_omap_port *up = dev_id;
+ unsigned int iir, lsr;
+ unsigned long flags;
+
+ iir = serial_in(up, UART_IIR);
+ if (iir & UART_IIR_NO_INT)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ lsr = serial_in(up, UART_LSR);
+ if (iir & UART_IIR_RLSI) {
+ if (!up->use_dma) {
+ if (lsr & UART_LSR_DR)
+ receive_chars(up, &lsr);
+ } else {
+ up->ier &= ~(UART_IER_RDI | UART_IER_RLSI);
+ serial_out(up, UART_IER, up->ier);
+ if ((serial_omap_start_rxdma(up) != 0) &&
+ (lsr & UART_LSR_DR))
+ receive_chars(up, &lsr);
+ }
+ }
+
+ check_modem_status(up);
+ if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI))
+ transmit_chars(up);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ up->port_activity = jiffies;
+ return IRQ_HANDLED;
+}
+
+static unsigned int serial_omap_tx_empty(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned long flags = 0;
+ unsigned int ret = 0;
+
+ dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->pdev->id);
+ spin_lock_irqsave(&up->port.lock, flags);
+ ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ return ret;
+}
+
+static unsigned int serial_omap_get_mctrl(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned char status;
+ unsigned int ret = 0;
+
+ status = check_modem_status(up);
+ dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id);
+
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+ return ret;
+}
+
+static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned char mcr = 0;
+
+ dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->pdev->id);
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (mctrl & TIOCM_OUT1)
+ mcr |= UART_MCR_OUT1;
+ if (mctrl & TIOCM_OUT2)
+ mcr |= UART_MCR_OUT2;
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ mcr |= up->mcr;
+ serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_omap_break_ctl(struct uart_port *port, int break_state)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned long flags = 0;
+
+ dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->pdev->id);
+ spin_lock_irqsave(&up->port.lock, flags);
+ if (break_state == -1)
+ up->lcr |= UART_LCR_SBC;
+ else
+ up->lcr &= ~UART_LCR_SBC;
+ serial_out(up, UART_LCR, up->lcr);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int serial_omap_startup(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned long flags = 0;
+ int retval;
+
+ /*
+ * Allocate the IRQ
+ */
+ retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags,
+ up->name, up);
+ if (retval)
+ return retval;
+
+ dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id);
+
+ /*
+ * Clear the FIFO buffers and disable them.
+ * (they will be reenabled in set_termios())
+ */
+ serial_omap_clear_fifos(up);
+ /* For Hardware flow control */
+ serial_out(up, UART_MCR, UART_MCR_RTS);
+
+ /*
+ * Clear the interrupt registers.
+ */
+ (void) serial_in(up, UART_LSR);
+ if (serial_in(up, UART_LSR) & UART_LSR_DR)
+ (void) serial_in(up, UART_RX);
+ (void) serial_in(up, UART_IIR);
+ (void) serial_in(up, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ serial_out(up, UART_LCR, UART_LCR_WLEN8);
+ spin_lock_irqsave(&up->port.lock, flags);
+ /*
+ * Most PC uarts need OUT2 raised to enable interrupts.
+ */
+ up->port.mctrl |= TIOCM_OUT2;
+ serial_omap_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ up->msr_saved_flags = 0;
+ if (up->use_dma) {
+ free_page((unsigned long)up->port.state->xmit.buf);
+ up->port.state->xmit.buf = dma_alloc_coherent(NULL,
+ UART_XMIT_SIZE,
+ (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys),
+ 0);
+ init_timer(&(up->uart_dma.rx_timer));
+ up->uart_dma.rx_timer.function = serial_omap_rx_timeout;
+ up->uart_dma.rx_timer.data = up->pdev->id;
+ /* Currently the buffer size is 4KB. Can increase it */
+ up->uart_dma.rx_buf = dma_alloc_coherent(NULL,
+ up->uart_dma.rx_buf_size,
+ (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0);
+ }
+ /*
+ * Finally, enable interrupts. Note: Modem status interrupts
+ * are set via set_termios(), which will be occurring imminently
+ * anyway, so we don't enable them here.
+ */
+ up->ier = UART_IER_RLSI | UART_IER_RDI;
+ serial_out(up, UART_IER, up->ier);
+
+ up->port_activity = jiffies;
+ return 0;
+}
+
+static void serial_omap_shutdown(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned long flags = 0;
+
+ dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->pdev->id);
+ /*
+ * Disable interrupts from this port
+ */
+ up->ier = 0;
+ serial_out(up, UART_IER, 0);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->port.mctrl &= ~TIOCM_OUT2;
+ serial_omap_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /*
+ * Disable break condition and FIFOs
+ */
+ serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+ serial_omap_clear_fifos(up);
+
+ /*
+ * Read data port to reset things, and then free the irq
+ */
+ if (serial_in(up, UART_LSR) & UART_LSR_DR)
+ (void) serial_in(up, UART_RX);
+ if (up->use_dma) {
+ dma_free_coherent(up->port.dev,
+ UART_XMIT_SIZE, up->port.state->xmit.buf,
+ up->uart_dma.tx_buf_dma_phys);
+ up->port.state->xmit.buf = NULL;
+ serial_omap_stop_rx(port);
+ dma_free_coherent(up->port.dev,
+ up->uart_dma.rx_buf_size, up->uart_dma.rx_buf,
+ up->uart_dma.rx_buf_dma_phys);
+ up->uart_dma.rx_buf = NULL;
+ }
+ free_irq(up->port.irq, up);
+}
+
+static inline void
+serial_omap_configure_xonxoff
+ (struct uart_omap_port *up, struct ktermios *termios)
+{
+ unsigned char efr = 0;
+
+ up->lcr = serial_in(up, UART_LCR);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+ up->efr = serial_in(up, UART_EFR);
+ serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB);
+
+ serial_out(up, UART_XON1, termios->c_cc[VSTART]);
+ serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
+
+ /* clear SW control mode bits */
+ efr = up->efr;
+ efr &= OMAP_UART_SW_CLR;
+
+ /*
+ * IXON Flag:
+ * Enable XON/XOFF flow control on output.
+ * Transmit XON1, XOFF1
+ */
+ if (termios->c_iflag & IXON)
+ efr |= OMAP_UART_SW_TX;
+
+ /*
+ * IXOFF Flag:
+ * Enable XON/XOFF flow control on input.
+ * Receiver compares XON1, XOFF1.
+ */
+ if (termios->c_iflag & IXOFF)
+ efr |= OMAP_UART_SW_RX;
+
+ serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+ serial_out(up, UART_LCR, UART_LCR_DLAB);
+
+ up->mcr = serial_in(up, UART_MCR);
+
+ /*
+ * IXANY Flag:
+ * Enable any character to restart output.
+ * Operation resumes after receiving any
+ * character after recognition of the XOFF character
+ */
+ if (termios->c_iflag & IXANY)
+ up->mcr |= UART_MCR_XONANY;
+
+ serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+ serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+ /* Enable special char function UARTi.EFR_REG[5] and
+ * load the new software flow control mode IXON or IXOFF
+ * and restore the UARTi.EFR_REG[4] ENHANCED_EN value.
+ */
+ serial_out(up, UART_EFR, efr | UART_EFR_SCD);
+ serial_out(up, UART_LCR, UART_LCR_DLAB);
+
+ serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR);
+ serial_out(up, UART_LCR, up->lcr);
+}
+
+static void
+serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned char cval = 0;
+ unsigned char efr = 0;
+ unsigned long flags = 0;
+ unsigned int baud, quot;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ cval = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ cval = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ cval |= UART_LCR_STOP;
+ if (termios->c_cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(termios->c_cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13);
+ quot = serial_omap_get_divisor(port, baud);
+
+ up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 |
+ UART_FCR_ENABLE_FIFO;
+ if (up->use_dma)
+ up->fcr |= UART_FCR_DMA_SELECT;
+
+ /*
+ * Ok, we're now changing the port state. Do it with
+ * interrupts disabled.
+ */
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /*
+ * Update the per-port timeout.
+ */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ if (termios->c_iflag & INPCK)
+ up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ up->port.read_status_mask |= UART_LSR_BI;
+
+ /*
+ * Characters to ignore
+ */
+ up->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (termios->c_iflag & IGNBRK) {
+ up->port.ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_OE;
+ }
+
+ /*
+ * ignore all characters if CREAD is not set
+ */
+ if ((termios->c_cflag & CREAD) == 0)
+ up->port.ignore_status_mask |= UART_LSR_DR;
+
+ /*
+ * Modem status interrupts
+ */
+ up->ier &= ~UART_IER_MSI;
+ if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+ up->ier |= UART_IER_MSI;
+ serial_out(up, UART_IER, up->ier);
+ serial_out(up, UART_LCR, cval); /* reset DLAB */
+
+ /* FIFOs and DMA Settings */
+
+ /* FCR can be changed only when the
+ * baud clock is not running
+ * DLL_REG and DLH_REG set to 0.
+ */
+ serial_out(up, UART_LCR, UART_LCR_DLAB);
+ serial_out(up, UART_DLL, 0);
+ serial_out(up, UART_DLM, 0);
+ serial_out(up, UART_LCR, 0);
+
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+
+ up->efr = serial_in(up, UART_EFR);
+ serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+
+ serial_out(up, UART_LCR, UART_LCR_DLAB);
+ up->mcr = serial_in(up, UART_MCR);
+ serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+ /* FIFO ENABLE, DMA MODE */
+ serial_out(up, UART_FCR, up->fcr);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+
+ if (up->use_dma) {
+ serial_out(up, UART_TI752_TLR, 0);
+ serial_out(up, UART_OMAP_SCR,
+ (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8));
+ }
+
+ serial_out(up, UART_EFR, up->efr);
+ serial_out(up, UART_LCR, UART_LCR_DLAB);
+ serial_out(up, UART_MCR, up->mcr);
+
+ /* Protocol, Baud Rate, and Interrupt Settings */
+
+ serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_DISABLE);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+
+ up->efr = serial_in(up, UART_EFR);
+ serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_IER, 0);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+
+ serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
+
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_IER, up->ier);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+
+ serial_out(up, UART_EFR, up->efr);
+ serial_out(up, UART_LCR, cval);
+
+ if (baud > 230400 && baud != 3000000)
+ serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_MODE13X);
+ else
+ serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_MODE16X);
+
+ /* Hardware Flow Control Configuration */
+
+ if (termios->c_cflag & CRTSCTS) {
+ efr |= (UART_EFR_CTS | UART_EFR_RTS);
+ serial_out(up, UART_LCR, UART_LCR_DLAB);
+
+ up->mcr = serial_in(up, UART_MCR);
+ serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+ up->efr = serial_in(up, UART_EFR);
+ serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+
+ serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+ serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */
+ serial_out(up, UART_LCR, UART_LCR_DLAB);
+ serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS);
+ serial_out(up, UART_LCR, cval);
+ }
+
+ serial_omap_set_mctrl(&up->port, up->port.mctrl);
+ /* Software Flow Control Configuration */
+ if (termios->c_iflag & (IXON | IXOFF))
+ serial_omap_configure_xonxoff(up, termios);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id);
+}
+
+static void
+serial_omap_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ unsigned char efr;
+
+ dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->pdev->id);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+ efr = serial_in(up, UART_EFR);
+ serial_out(up, UART_EFR, efr | UART_EFR_ECB);
+ serial_out(up, UART_LCR, 0);
+
+ serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
+ serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB);
+ serial_out(up, UART_EFR, efr);
+ serial_out(up, UART_LCR, 0);
+ /* Enable module level wake up */
+ serial_out(up, UART_OMAP_WER,
+ (state != 0) ? OMAP_UART_WER_MOD_WKUP : 0);
+}
+
+static void serial_omap_release_port(struct uart_port *port)
+{
+ dev_dbg(port->dev, "serial_omap_release_port+\n");
+}
+
+static int serial_omap_request_port(struct uart_port *port)
+{
+ dev_dbg(port->dev, "serial_omap_request_port+\n");
+ return 0;
+}
+
+static void serial_omap_config_port(struct uart_port *port, int flags)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+ dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
+ up->pdev->id);
+ up->port.type = PORT_OMAP;
+}
+
+static int
+serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* we don't want the core code to modify any port params */
+ dev_dbg(port->dev, "serial_omap_verify_port+\n");
+ return -EINVAL;
+}
+
+static const char *
+serial_omap_type(struct uart_port *port)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+ dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->pdev->id);
+ return up->name;
+}
+
+#ifdef CONFIG_SERIAL_OMAP_CONSOLE
+
+static struct uart_omap_port *serial_omap_console_ports[4];
+
+static struct uart_driver serial_omap_reg;
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+static inline void wait_for_xmitr(struct uart_omap_port *up)
+{
+ unsigned int status, tmout = 10000;
+
+ /* Wait up to 10ms for the character(s) to be sent. */
+ do {
+ status = serial_in(up, UART_LSR);
+
+ if (status & UART_LSR_BI)
+ up->lsr_break_flag = UART_LSR_BI;
+
+ if (--tmout == 0)
+ break;
+ udelay(1);
+ } while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+ /* Wait up to 1s for flow control if necessary */
+ if (up->port.flags & UPF_CONS_FLOW) {
+ tmout = 1000000;
+ for (tmout = 1000000; tmout; tmout--) {
+ unsigned int msr = serial_in(up, UART_MSR);
+
+ up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
+ if (msr & UART_MSR_CTS)
+ break;
+
+ udelay(1);
+ }
+ }
+}
+
+static void serial_omap_console_putchar(struct uart_port *port, int ch)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+ wait_for_xmitr(up);
+ serial_out(up, UART_TX, ch);
+}
+
+static void
+serial_omap_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_omap_port *up = serial_omap_console_ports[co->index];
+ unsigned long flags;
+ unsigned int ier;
+ int locked = 1;
+
+ local_irq_save(flags);
+ if (up->port.sysrq)
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock(&up->port.lock);
+ else
+ spin_lock(&up->port.lock);
+
+ /*
+ * First save the IER then disable the interrupts
+ */
+ ier = serial_in(up, UART_IER);
+ serial_out(up, UART_IER, 0);
+
+ uart_console_write(&up->port, s, count, serial_omap_console_putchar);
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the IER
+ */
+ wait_for_xmitr(up);
+ serial_out(up, UART_IER, ier);
+ /*
+ * The receive handling will happen properly because the
+ * receive ready bit will still be set; it is not cleared
+ * on read. However, modem control will not, we must
+ * call it if we have saved something in the saved flags
+ * while processing with interrupts off.
+ */
+ if (up->msr_saved_flags)
+ check_modem_status(up);
+
+ if (locked)
+ spin_unlock(&up->port.lock);
+ local_irq_restore(flags);
+}
+
+static int __init
+serial_omap_console_setup(struct console *co, char *options)
+{
+ struct uart_omap_port *up;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (serial_omap_console_ports[co->index] == NULL)
+ return -ENODEV;
+ up = serial_omap_console_ports[co->index];
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_omap_console = {
+ .name = OMAP_SERIAL_NAME,
+ .write = serial_omap_console_write,
+ .device = uart_console_device,
+ .setup = serial_omap_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &serial_omap_reg,
+};
+
+static void serial_omap_add_console_port(struct uart_omap_port *up)
+{
+ serial_omap_console_ports[up->pdev->id] = up;
+}
+
+#define OMAP_CONSOLE (&serial_omap_console)
+
+#else
+
+#define OMAP_CONSOLE NULL
+
+static inline void serial_omap_add_console_port(struct uart_omap_port *up)
+{}
+
+#endif
+
+static struct uart_ops serial_omap_pops = {
+ .tx_empty = serial_omap_tx_empty,
+ .set_mctrl = serial_omap_set_mctrl,
+ .get_mctrl = serial_omap_get_mctrl,
+ .stop_tx = serial_omap_stop_tx,
+ .start_tx = serial_omap_start_tx,
+ .stop_rx = serial_omap_stop_rx,
+ .enable_ms = serial_omap_enable_ms,
+ .break_ctl = serial_omap_break_ctl,
+ .startup = serial_omap_startup,
+ .shutdown = serial_omap_shutdown,
+ .set_termios = serial_omap_set_termios,
+ .pm = serial_omap_pm,
+ .type = serial_omap_type,
+ .release_port = serial_omap_release_port,
+ .request_port = serial_omap_request_port,
+ .config_port = serial_omap_config_port,
+ .verify_port = serial_omap_verify_port,
+};
+
+static struct uart_driver serial_omap_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "OMAP-SERIAL",
+ .dev_name = OMAP_SERIAL_NAME,
+ .nr = OMAP_MAX_HSUART_PORTS,
+ .cons = OMAP_CONSOLE,
+};
+
+static int
+serial_omap_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct uart_omap_port *up = platform_get_drvdata(pdev);
+
+ if (up)
+ uart_suspend_port(&serial_omap_reg, &up->port);
+ return 0;
+}
+
+static int serial_omap_resume(struct platform_device *dev)
+{
+ struct uart_omap_port *up = platform_get_drvdata(dev);
+
+ if (up)
+ uart_resume_port(&serial_omap_reg, &up->port);
+ return 0;
+}
+
+static void serial_omap_rx_timeout(unsigned long uart_no)
+{
+ struct uart_omap_port *up = ui[uart_no];
+ unsigned int curr_dma_pos, curr_transmitted_size;
+ int ret = 0;
+
+ curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel);
+ if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) ||
+ (curr_dma_pos == 0)) {
+ if (jiffies_to_msecs(jiffies - up->port_activity) <
+ RX_TIMEOUT) {
+ mod_timer(&up->uart_dma.rx_timer, jiffies +
+ usecs_to_jiffies(up->uart_dma.rx_timeout));
+ } else {
+ serial_omap_stop_rxdma(up);
+ up->ier |= (UART_IER_RDI | UART_IER_RLSI);
+ serial_out(up, UART_IER, up->ier);
+ }
+ return;
+ }
+
+ curr_transmitted_size = curr_dma_pos -
+ up->uart_dma.prev_rx_dma_pos;
+ up->port.icount.rx += curr_transmitted_size;
+ tty_insert_flip_string(up->port.state->port.tty,
+ up->uart_dma.rx_buf +
+ (up->uart_dma.prev_rx_dma_pos -
+ up->uart_dma.rx_buf_dma_phys),
+ curr_transmitted_size);
+ tty_flip_buffer_push(up->port.state->port.tty);
+ up->uart_dma.prev_rx_dma_pos = curr_dma_pos;
+ if (up->uart_dma.rx_buf_size +
+ up->uart_dma.rx_buf_dma_phys == curr_dma_pos) {
+ ret = serial_omap_start_rxdma(up);
+ if (ret < 0) {
+ serial_omap_stop_rxdma(up);
+ up->ier |= (UART_IER_RDI | UART_IER_RLSI);
+ serial_out(up, UART_IER, up->ier);
+ }
+ } else {
+ mod_timer(&up->uart_dma.rx_timer, jiffies +
+ usecs_to_jiffies(up->uart_dma.rx_timeout));
+ }
+ up->port_activity = jiffies;
+}
+
+static void uart_rx_dma_callback(int lch, u16 ch_status, void *data)
+{
+ return;
+}
+
+static int serial_omap_start_rxdma(struct uart_omap_port *up)
+{
+ int ret = 0;
+
+ if (up->uart_dma.rx_dma_channel == -1) {
+ ret = omap_request_dma(up->uart_dma.uart_dma_rx,
+ "UART Rx DMA",
+ (void *)uart_rx_dma_callback, up,
+ &(up->uart_dma.rx_dma_channel));
+ if (ret < 0)
+ return ret;
+
+ omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0,
+ OMAP_DMA_AMODE_CONSTANT,
+ up->uart_dma.uart_base, 0, 0);
+ omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0,
+ OMAP_DMA_AMODE_POST_INC,
+ up->uart_dma.rx_buf_dma_phys, 0, 0);
+ omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel,
+ OMAP_DMA_DATA_TYPE_S8,
+ up->uart_dma.rx_buf_size, 1,
+ OMAP_DMA_SYNC_ELEMENT,
+ up->uart_dma.uart_dma_rx, 0);
+ }
+ up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys;
+ /* FIXME: Cache maintenance needed here? */
+ omap_start_dma(up->uart_dma.rx_dma_channel);
+ mod_timer(&up->uart_dma.rx_timer, jiffies +
+ usecs_to_jiffies(up->uart_dma.rx_timeout));
+ up->uart_dma.rx_dma_used = true;
+ return ret;
+}
+
+static void serial_omap_continue_tx(struct uart_omap_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ unsigned int start = up->uart_dma.tx_buf_dma_phys
+ + (xmit->tail & (UART_XMIT_SIZE - 1));
+
+ if (uart_circ_empty(xmit))
+ return;
+
+ up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit);
+ /*
+ * It is a circular buffer. See if the buffer has wounded back.
+ * If yes it will have to be transferred in two separate dma
+ * transfers
+ */
+ if (start + up->uart_dma.tx_buf_size >=
+ up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE)
+ up->uart_dma.tx_buf_size =
+ (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start;
+ omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0,
+ OMAP_DMA_AMODE_CONSTANT,
+ up->uart_dma.uart_base, 0, 0);
+ omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0,
+ OMAP_DMA_AMODE_POST_INC, start, 0, 0);
+ omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel,
+ OMAP_DMA_DATA_TYPE_S8,
+ up->uart_dma.tx_buf_size, 1,
+ OMAP_DMA_SYNC_ELEMENT,
+ up->uart_dma.uart_dma_tx, 0);
+ /* FIXME: Cache maintenance needed here? */
+ omap_start_dma(up->uart_dma.tx_dma_channel);
+}
+
+static void uart_tx_dma_callback(int lch, u16 ch_status, void *data)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)data;
+ struct circ_buf *xmit = &up->port.state->xmit;
+
+ xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \
+ (UART_XMIT_SIZE - 1);
+ up->port.icount.tx += up->uart_dma.tx_buf_size;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (uart_circ_empty(xmit)) {
+ spin_lock(&(up->uart_dma.tx_lock));
+ serial_omap_stop_tx(&up->port);
+ up->uart_dma.tx_dma_used = false;
+ spin_unlock(&(up->uart_dma.tx_lock));
+ } else {
+ omap_stop_dma(up->uart_dma.tx_dma_channel);
+ serial_omap_continue_tx(up);
+ }
+ up->port_activity = jiffies;
+ return;
+}
+
+static int serial_omap_probe(struct platform_device *pdev)
+{
+ struct uart_omap_port *up;
+ struct resource *mem, *irq, *dma_tx, *dma_rx;
+ struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
+ int ret = -ENOSPC;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "memory region already claimed\n");
+ return -EBUSY;
+ }
+
+ dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
+ if (!dma_rx) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
+ if (!dma_tx) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ up = kzalloc(sizeof(*up), GFP_KERNEL);
+ if (up == NULL) {
+ ret = -ENOMEM;
+ goto do_release_region;
+ }
+ sprintf(up->name, "OMAP UART%d", pdev->id);
+ up->pdev = pdev;
+ up->port.dev = &pdev->dev;
+ up->port.type = PORT_OMAP;
+ up->port.iotype = UPIO_MEM;
+ up->port.irq = irq->start;
+
+ up->port.regshift = 2;
+ up->port.fifosize = 64;
+ up->port.ops = &serial_omap_pops;
+ up->port.line = pdev->id;
+
+ up->port.membase = omap_up_info->membase;
+ up->port.mapbase = omap_up_info->mapbase;
+ up->port.flags = omap_up_info->flags;
+ up->port.irqflags = omap_up_info->irqflags;
+ up->port.uartclk = omap_up_info->uartclk;
+ up->uart_dma.uart_base = mem->start;
+
+ if (omap_up_info->dma_enabled) {
+ up->uart_dma.uart_dma_tx = dma_tx->start;
+ up->uart_dma.uart_dma_rx = dma_rx->start;
+ up->use_dma = 1;
+ up->uart_dma.rx_buf_size = 4096;
+ up->uart_dma.rx_timeout = 2;
+ spin_lock_init(&(up->uart_dma.tx_lock));
+ spin_lock_init(&(up->uart_dma.rx_lock));
+ up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
+ up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
+ }
+
+ ui[pdev->id] = up;
+ serial_omap_add_console_port(up);
+
+ ret = uart_add_one_port(&serial_omap_reg, &up->port);
+ if (ret != 0)
+ goto do_release_region;
+
+ platform_set_drvdata(pdev, up);
+ return 0;
+err:
+ dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
+ pdev->id, __func__, ret);
+do_release_region:
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return ret;
+}
+
+static int serial_omap_remove(struct platform_device *dev)
+{
+ struct uart_omap_port *up = platform_get_drvdata(dev);
+
+ platform_set_drvdata(dev, NULL);
+ if (up) {
+ uart_remove_one_port(&serial_omap_reg, &up->port);
+ kfree(up);
+ }
+ return 0;
+}
+
+static struct platform_driver serial_omap_driver = {
+ .probe = serial_omap_probe,
+ .remove = serial_omap_remove,
+
+ .suspend = serial_omap_suspend,
+ .resume = serial_omap_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init serial_omap_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&serial_omap_reg);
+ if (ret != 0)
+ return ret;
+ ret = platform_driver_register(&serial_omap_driver);
+ if (ret != 0)
+ uart_unregister_driver(&serial_omap_reg);
+ return ret;
+}
+
+static void __exit serial_omap_exit(void)
+{
+ platform_driver_unregister(&serial_omap_driver);
+ uart_unregister_driver(&serial_omap_reg);
+}
+
+module_init(serial_omap_init);
+module_exit(serial_omap_exit);
+
+MODULE_DESCRIPTION("OMAP High Speed UART driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments Inc");
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index 9b52f77a9305..d2352ac437c5 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -140,7 +140,15 @@
# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
# define SCSPTR1 0xffe10024 /* 16 bit SCIF */
# define SCIF_ORER 0x0001 /* Overrun error bit */
-# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
+
+#if defined(CONFIG_SH_SH2007)
+/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */
+# define SCSCR_INIT(port) 0x38
+#else
+/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */
+# define SCSCR_INIT(port) 0x3a
+#endif
+
#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \
defined(CONFIG_CPU_SUBTYPE_SH7786)
# define SCSPTR0 0xffea0024 /* 16 bit SCIF */
@@ -616,9 +624,10 @@ static inline int sci_rxd_in(struct uart_port *port)
* -- Mitch Davis - 15 Jul 2000
*/
-#if defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7785) || \
- defined(CONFIG_CPU_SUBTYPE_SH7786)
+#if (defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786)) && \
+ !defined(CONFIG_SH_SH2007)
#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1)
#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig
index a54de0b9b3df..f168a6159961 100644
--- a/drivers/sh/Kconfig
+++ b/drivers/sh/Kconfig
@@ -1,24 +1,5 @@
-config INTC_USERIMASK
- bool "Userspace interrupt masking support"
- depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
- help
- This enables support for hardware-assisted userspace hardirq
- masking.
+menu "SuperH / SH-Mobile Driver Options"
- SH-4A and newer interrupt blocks all support a special shadowed
- page with all non-masking registers obscured when mapped in to
- userspace. This is primarily for use by userspace device
- drivers that are using special priority levels.
+source "drivers/sh/intc/Kconfig"
- If in doubt, say N.
-
-config INTC_BALANCING
- bool "Hardware IRQ balancing support"
- depends on SMP && SUPERH && CPU_SUBTYPE_SH7786
- help
- This enables support for IRQ auto-distribution mode on SH-X3
- SMP parts. All of the balancing and CPU wakeup decisions are
- taken care of automatically by hardware for distributed
- vectors.
-
- If in doubt, say N.
+endmenu
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 08fc653a825c..24e6cec0ae8d 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -1,10 +1,9 @@
#
# Makefile for the SuperH specific drivers.
#
-obj-y := clk.o intc.o
+obj-y := intc/
-obj-$(CONFIG_SUPERHYWAY) += superhyway/
+obj-$(CONFIG_HAVE_CLK) += clk/
obj-$(CONFIG_MAPLE) += maple/
-
+obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_GENERIC_GPIO) += pfc.o
-obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o
diff --git a/drivers/sh/clk/Makefile b/drivers/sh/clk/Makefile
new file mode 100644
index 000000000000..5d15ebfaa074
--- /dev/null
+++ b/drivers/sh/clk/Makefile
@@ -0,0 +1,3 @@
+obj-y := core.o
+
+obj-$(CONFIG_SH_CLK_CPG) += cpg.o
diff --git a/drivers/sh/clk.c b/drivers/sh/clk/core.c
index 5d84adac9ec4..fd0d1b98901c 100644
--- a/drivers/sh/clk.c
+++ b/drivers/sh/clk/core.c
@@ -1,7 +1,7 @@
/*
- * drivers/sh/clk.c - SuperH clock framework
+ * SuperH clock framework
*
- * Copyright (C) 2005 - 2009 Paul Mundt
+ * Copyright (C) 2005 - 2010 Paul Mundt
*
* This clock framework is derived from the OMAP version by:
*
@@ -14,6 +14,8 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
+#define pr_fmt(fmt) "clock: " fmt
+
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -23,7 +25,7 @@
#include <linux/sysdev.h>
#include <linux/seq_file.h>
#include <linux/err.h>
-#include <linux/platform_device.h>
+#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/cpufreq.h>
#include <linux/clk.h>
@@ -43,6 +45,8 @@ void clk_rate_table_build(struct clk *clk,
unsigned long freq;
int i;
+ clk->nr_freqs = nr_freqs;
+
for (i = 0; i < nr_freqs; i++) {
div = 1;
mult = 1;
@@ -67,29 +71,39 @@ void clk_rate_table_build(struct clk *clk,
freq_table[i].frequency = CPUFREQ_TABLE_END;
}
-long clk_rate_table_round(struct clk *clk,
- struct cpufreq_frequency_table *freq_table,
- unsigned long rate)
+struct clk_rate_round_data;
+
+struct clk_rate_round_data {
+ unsigned long rate;
+ unsigned int min, max;
+ long (*func)(unsigned int, struct clk_rate_round_data *);
+ void *arg;
+};
+
+#define for_each_frequency(pos, r, freq) \
+ for (pos = r->min, freq = r->func(pos, r); \
+ pos <= r->max; pos++, freq = r->func(pos, r)) \
+ if (unlikely(freq == 0)) \
+ ; \
+ else
+
+static long clk_rate_round_helper(struct clk_rate_round_data *rounder)
{
unsigned long rate_error, rate_error_prev = ~0UL;
- unsigned long rate_best_fit = rate;
- unsigned long highest, lowest;
+ unsigned long rate_best_fit = rounder->rate;
+ unsigned long highest, lowest, freq;
int i;
- highest = lowest = 0;
-
- for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
- unsigned long freq = freq_table[i].frequency;
-
- if (freq == CPUFREQ_ENTRY_INVALID)
- continue;
+ highest = 0;
+ lowest = ~0UL;
+ for_each_frequency(i, rounder, freq) {
if (freq > highest)
highest = freq;
if (freq < lowest)
lowest = freq;
- rate_error = abs(freq - rate);
+ rate_error = abs(freq - rounder->rate);
if (rate_error < rate_error_prev) {
rate_best_fit = freq;
rate_error_prev = rate_error;
@@ -99,14 +113,64 @@ long clk_rate_table_round(struct clk *clk,
break;
}
- if (rate >= highest)
+ if (rounder->rate >= highest)
rate_best_fit = highest;
- if (rate <= lowest)
+ if (rounder->rate <= lowest)
rate_best_fit = lowest;
return rate_best_fit;
}
+static long clk_rate_table_iter(unsigned int pos,
+ struct clk_rate_round_data *rounder)
+{
+ struct cpufreq_frequency_table *freq_table = rounder->arg;
+ unsigned long freq = freq_table[pos].frequency;
+
+ if (freq == CPUFREQ_ENTRY_INVALID)
+ freq = 0;
+
+ return freq;
+}
+
+long clk_rate_table_round(struct clk *clk,
+ struct cpufreq_frequency_table *freq_table,
+ unsigned long rate)
+{
+ struct clk_rate_round_data table_round = {
+ .min = 0,
+ .max = clk->nr_freqs - 1,
+ .func = clk_rate_table_iter,
+ .arg = freq_table,
+ .rate = rate,
+ };
+
+ if (clk->nr_freqs < 1)
+ return 0;
+
+ return clk_rate_round_helper(&table_round);
+}
+
+static long clk_rate_div_range_iter(unsigned int pos,
+ struct clk_rate_round_data *rounder)
+{
+ return clk_get_rate(rounder->arg) / pos;
+}
+
+long clk_rate_div_range_round(struct clk *clk, unsigned int div_min,
+ unsigned int div_max, unsigned long rate)
+{
+ struct clk_rate_round_data div_range_round = {
+ .min = div_min,
+ .max = div_max,
+ .func = clk_rate_div_range_iter,
+ .arg = clk_get_parent(clk),
+ .rate = rate,
+ };
+
+ return clk_rate_round_helper(&div_range_round);
+}
+
int clk_rate_table_find(struct clk *clk,
struct cpufreq_frequency_table *freq_table,
unsigned long rate)
@@ -160,8 +224,8 @@ void propagate_rate(struct clk *tclk)
static void __clk_disable(struct clk *clk)
{
- if (WARN(!clk->usecount, "Trying to disable clock %s with 0 usecount\n",
- clk->name))
+ if (WARN(!clk->usecount, "Trying to disable clock %p with 0 usecount\n",
+ clk))
return;
if (!(--clk->usecount)) {
@@ -248,8 +312,88 @@ void recalculate_root_clocks(void)
}
}
+static struct clk_mapping dummy_mapping;
+
+static struct clk *lookup_root_clock(struct clk *clk)
+{
+ while (clk->parent)
+ clk = clk->parent;
+
+ return clk;
+}
+
+static int clk_establish_mapping(struct clk *clk)
+{
+ struct clk_mapping *mapping = clk->mapping;
+
+ /*
+ * Propagate mappings.
+ */
+ if (!mapping) {
+ struct clk *clkp;
+
+ /*
+ * dummy mapping for root clocks with no specified ranges
+ */
+ if (!clk->parent) {
+ clk->mapping = &dummy_mapping;
+ return 0;
+ }
+
+ /*
+ * If we're on a child clock and it provides no mapping of its
+ * own, inherit the mapping from its root clock.
+ */
+ clkp = lookup_root_clock(clk);
+ mapping = clkp->mapping;
+ BUG_ON(!mapping);
+ }
+
+ /*
+ * Establish initial mapping.
+ */
+ if (!mapping->base && mapping->phys) {
+ kref_init(&mapping->ref);
+
+ mapping->base = ioremap_nocache(mapping->phys, mapping->len);
+ if (unlikely(!mapping->base))
+ return -ENXIO;
+ } else if (mapping->base) {
+ /*
+ * Bump the refcount for an existing mapping
+ */
+ kref_get(&mapping->ref);
+ }
+
+ clk->mapping = mapping;
+ return 0;
+}
+
+static void clk_destroy_mapping(struct kref *kref)
+{
+ struct clk_mapping *mapping;
+
+ mapping = container_of(kref, struct clk_mapping, ref);
+
+ iounmap(mapping->base);
+}
+
+static void clk_teardown_mapping(struct clk *clk)
+{
+ struct clk_mapping *mapping = clk->mapping;
+
+ /* Nothing to do */
+ if (mapping == &dummy_mapping)
+ return;
+
+ kref_put(&mapping->ref, clk_destroy_mapping);
+ clk->mapping = NULL;
+}
+
int clk_register(struct clk *clk)
{
+ int ret;
+
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
@@ -264,6 +408,10 @@ int clk_register(struct clk *clk)
INIT_LIST_HEAD(&clk->children);
clk->usecount = 0;
+ ret = clk_establish_mapping(clk);
+ if (unlikely(ret))
+ goto out_unlock;
+
if (clk->parent)
list_add(&clk->sibling, &clk->parent->children);
else
@@ -272,9 +420,11 @@ int clk_register(struct clk *clk)
list_add(&clk->node, &clock_list);
if (clk->ops && clk->ops->init)
clk->ops->init(clk);
+
+out_unlock:
mutex_unlock(&clock_list_sem);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(clk_register);
@@ -283,6 +433,7 @@ void clk_unregister(struct clk *clk)
mutex_lock(&clock_list_sem);
list_del(&clk->sibling);
list_del(&clk->node);
+ clk_teardown_mapping(clk);
mutex_unlock(&clock_list_sem);
}
EXPORT_SYMBOL_GPL(clk_unregister);
@@ -354,10 +505,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
ret = clk_reparent(clk, parent);
if (ret == 0) {
- pr_debug("clock: set parent of %s to %s (new rate %ld)\n",
- clk->name, clk->parent->name, clk->rate);
if (clk->ops->recalc)
clk->rate = clk->ops->recalc(clk);
+ pr_debug("set parent of %p to %p (new rate %ld)\n",
+ clk, clk->parent, clk->rate);
propagate_rate(clk);
}
} else
@@ -469,9 +620,7 @@ static int clk_debugfs_register_one(struct clk *c)
char s[255];
char *p = s;
- p += sprintf(p, "%s", c->name);
- if (c->id >= 0)
- sprintf(p, ":%d", c->id);
+ p += sprintf(p, "%p", c);
d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root);
if (!d)
return -ENOMEM;
@@ -513,7 +662,7 @@ static int clk_debugfs_register(struct clk *c)
return err;
}
- if (!c->dentry && c->name) {
+ if (!c->dentry) {
err = clk_debugfs_register_one(c);
if (err)
return err;
diff --git a/drivers/sh/clk-cpg.c b/drivers/sh/clk/cpg.c
index 8c024b984ed8..3aea5f0ceb09 100644
--- a/drivers/sh/clk-cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -1,3 +1,12 @@
+/*
+ * Helper routines for SuperH Clock Pulse Generator blocks (CPG).
+ *
+ * Copyright (C) 2010 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
#include <linux/clk.h>
#include <linux/compiler.h>
#include <linux/slab.h>
@@ -180,7 +189,6 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
clkp = clks + k;
clkp->ops = ops;
- clkp->id = -1;
clkp->freq_table = freq_table + (k * freq_table_size);
clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
@@ -319,7 +327,6 @@ static int __init sh_clk_div4_register_ops(struct clk *clks, int nr,
clkp = clks + k;
clkp->ops = ops;
- clkp->id = -1;
clkp->priv = table;
clkp->freq_table = freq_table + (k * freq_table_size);
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
deleted file mode 100644
index e91a23e5ffd8..000000000000
--- a/drivers/sh/intc.c
+++ /dev/null
@@ -1,1390 +0,0 @@
-/*
- * Shared interrupt handling code for IPR and INTC2 types of IRQs.
- *
- * Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
- *
- * Based on intc2.c and ipr.c
- *
- * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi
- * Copyright (C) 2000 Kazumoto Kojima
- * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
- * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
- * Copyright (C) 2005, 2006 Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/sh_intc.h>
-#include <linux/sysdev.h>
-#include <linux/list.h>
-#include <linux/topology.h>
-#include <linux/bitmap.h>
-#include <linux/cpumask.h>
-#include <asm/sizes.h>
-
-#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
- ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
- ((addr_e) << 16) | ((addr_d << 24)))
-
-#define _INTC_SHIFT(h) (h & 0x1f)
-#define _INTC_WIDTH(h) ((h >> 5) & 0xf)
-#define _INTC_FN(h) ((h >> 9) & 0xf)
-#define _INTC_MODE(h) ((h >> 13) & 0x7)
-#define _INTC_ADDR_E(h) ((h >> 16) & 0xff)
-#define _INTC_ADDR_D(h) ((h >> 24) & 0xff)
-
-struct intc_handle_int {
- unsigned int irq;
- unsigned long handle;
-};
-
-struct intc_window {
- phys_addr_t phys;
- void __iomem *virt;
- unsigned long size;
-};
-
-struct intc_desc_int {
- struct list_head list;
- struct sys_device sysdev;
- pm_message_t state;
- unsigned long *reg;
-#ifdef CONFIG_SMP
- unsigned long *smp;
-#endif
- unsigned int nr_reg;
- struct intc_handle_int *prio;
- unsigned int nr_prio;
- struct intc_handle_int *sense;
- unsigned int nr_sense;
- struct intc_window *window;
- unsigned int nr_windows;
- struct irq_chip chip;
-};
-
-static LIST_HEAD(intc_list);
-
-/*
- * The intc_irq_map provides a global map of bound IRQ vectors for a
- * given platform. Allocation of IRQs are either static through the CPU
- * vector map, or dynamic in the case of board mux vectors or MSI.
- *
- * As this is a central point for all IRQ controllers on the system,
- * each of the available sources are mapped out here. This combined with
- * sparseirq makes it quite trivial to keep the vector map tightly packed
- * when dynamically creating IRQs, as well as tying in to otherwise
- * unused irq_desc positions in the sparse array.
- */
-static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
-static DEFINE_SPINLOCK(vector_lock);
-
-#ifdef CONFIG_SMP
-#define IS_SMP(x) x.smp
-#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c))
-#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1)
-#else
-#define IS_SMP(x) 0
-#define INTC_REG(d, x, c) (d->reg[(x)])
-#define SMP_NR(d, x) 1
-#endif
-
-static unsigned int intc_prio_level[NR_IRQS]; /* for now */
-static unsigned int default_prio_level = 2; /* 2 - 16 */
-static unsigned long ack_handle[NR_IRQS];
-#ifdef CONFIG_INTC_BALANCING
-static unsigned long dist_handle[NR_IRQS];
-#endif
-
-static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
-{
- struct irq_chip *chip = get_irq_chip(irq);
- return container_of(chip, struct intc_desc_int, chip);
-}
-
-static unsigned long intc_phys_to_virt(struct intc_desc_int *d,
- unsigned long address)
-{
- struct intc_window *window;
- int k;
-
- /* scan through physical windows and convert address */
- for (k = 0; k < d->nr_windows; k++) {
- window = d->window + k;
-
- if (address < window->phys)
- continue;
-
- if (address >= (window->phys + window->size))
- continue;
-
- address -= window->phys;
- address += (unsigned long)window->virt;
-
- return address;
- }
-
- /* no windows defined, register must be 1:1 mapped virt:phys */
- return address;
-}
-
-static unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address)
-{
- unsigned int k;
-
- address = intc_phys_to_virt(d, address);
-
- for (k = 0; k < d->nr_reg; k++) {
- if (d->reg[k] == address)
- return k;
- }
-
- BUG();
- return 0;
-}
-
-static inline unsigned int set_field(unsigned int value,
- unsigned int field_value,
- unsigned int handle)
-{
- unsigned int width = _INTC_WIDTH(handle);
- unsigned int shift = _INTC_SHIFT(handle);
-
- value &= ~(((1 << width) - 1) << shift);
- value |= field_value << shift;
- return value;
-}
-
-static void write_8(unsigned long addr, unsigned long h, unsigned long data)
-{
- __raw_writeb(set_field(0, data, h), addr);
- (void)__raw_readb(addr); /* Defeat write posting */
-}
-
-static void write_16(unsigned long addr, unsigned long h, unsigned long data)
-{
- __raw_writew(set_field(0, data, h), addr);
- (void)__raw_readw(addr); /* Defeat write posting */
-}
-
-static void write_32(unsigned long addr, unsigned long h, unsigned long data)
-{
- __raw_writel(set_field(0, data, h), addr);
- (void)__raw_readl(addr); /* Defeat write posting */
-}
-
-static void modify_8(unsigned long addr, unsigned long h, unsigned long data)
-{
- unsigned long flags;
- local_irq_save(flags);
- __raw_writeb(set_field(__raw_readb(addr), data, h), addr);
- (void)__raw_readb(addr); /* Defeat write posting */
- local_irq_restore(flags);
-}
-
-static void modify_16(unsigned long addr, unsigned long h, unsigned long data)
-{
- unsigned long flags;
- local_irq_save(flags);
- __raw_writew(set_field(__raw_readw(addr), data, h), addr);
- (void)__raw_readw(addr); /* Defeat write posting */
- local_irq_restore(flags);
-}
-
-static void modify_32(unsigned long addr, unsigned long h, unsigned long data)
-{
- unsigned long flags;
- local_irq_save(flags);
- __raw_writel(set_field(__raw_readl(addr), data, h), addr);
- (void)__raw_readl(addr); /* Defeat write posting */
- local_irq_restore(flags);
-}
-
-enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 };
-
-static void (*intc_reg_fns[])(unsigned long addr,
- unsigned long h,
- unsigned long data) = {
- [REG_FN_WRITE_BASE + 0] = write_8,
- [REG_FN_WRITE_BASE + 1] = write_16,
- [REG_FN_WRITE_BASE + 3] = write_32,
- [REG_FN_MODIFY_BASE + 0] = modify_8,
- [REG_FN_MODIFY_BASE + 1] = modify_16,
- [REG_FN_MODIFY_BASE + 3] = modify_32,
-};
-
-enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */
- MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */
- MODE_DUAL_REG, /* Two registers, set bit to enable / disable */
- MODE_PRIO_REG, /* Priority value written to enable interrupt */
- MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */
-};
-
-static void intc_mode_field(unsigned long addr,
- unsigned long handle,
- void (*fn)(unsigned long,
- unsigned long,
- unsigned long),
- unsigned int irq)
-{
- fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
-}
-
-static void intc_mode_zero(unsigned long addr,
- unsigned long handle,
- void (*fn)(unsigned long,
- unsigned long,
- unsigned long),
- unsigned int irq)
-{
- fn(addr, handle, 0);
-}
-
-static void intc_mode_prio(unsigned long addr,
- unsigned long handle,
- void (*fn)(unsigned long,
- unsigned long,
- unsigned long),
- unsigned int irq)
-{
- fn(addr, handle, intc_prio_level[irq]);
-}
-
-static void (*intc_enable_fns[])(unsigned long addr,
- unsigned long handle,
- void (*fn)(unsigned long,
- unsigned long,
- unsigned long),
- unsigned int irq) = {
- [MODE_ENABLE_REG] = intc_mode_field,
- [MODE_MASK_REG] = intc_mode_zero,
- [MODE_DUAL_REG] = intc_mode_field,
- [MODE_PRIO_REG] = intc_mode_prio,
- [MODE_PCLR_REG] = intc_mode_prio,
-};
-
-static void (*intc_disable_fns[])(unsigned long addr,
- unsigned long handle,
- void (*fn)(unsigned long,
- unsigned long,
- unsigned long),
- unsigned int irq) = {
- [MODE_ENABLE_REG] = intc_mode_zero,
- [MODE_MASK_REG] = intc_mode_field,
- [MODE_DUAL_REG] = intc_mode_field,
- [MODE_PRIO_REG] = intc_mode_zero,
- [MODE_PCLR_REG] = intc_mode_field,
-};
-
-#ifdef CONFIG_INTC_BALANCING
-static inline void intc_balancing_enable(unsigned int irq)
-{
- struct intc_desc_int *d = get_intc_desc(irq);
- unsigned long handle = dist_handle[irq];
- unsigned long addr;
-
- if (irq_balancing_disabled(irq) || !handle)
- return;
-
- addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
- intc_reg_fns[_INTC_FN(handle)](addr, handle, 1);
-}
-
-static inline void intc_balancing_disable(unsigned int irq)
-{
- struct intc_desc_int *d = get_intc_desc(irq);
- unsigned long handle = dist_handle[irq];
- unsigned long addr;
-
- if (irq_balancing_disabled(irq) || !handle)
- return;
-
- addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
- intc_reg_fns[_INTC_FN(handle)](addr, handle, 0);
-}
-
-static unsigned int intc_dist_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id)
-{
- struct intc_mask_reg *mr = desc->hw.mask_regs;
- unsigned int i, j, fn, mode;
- unsigned long reg_e, reg_d;
-
- for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) {
- mr = desc->hw.mask_regs + i;
-
- /*
- * Skip this entry if there's no auto-distribution
- * register associated with it.
- */
- if (!mr->dist_reg)
- continue;
-
- for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
- if (mr->enum_ids[j] != enum_id)
- continue;
-
- fn = REG_FN_MODIFY_BASE;
- mode = MODE_ENABLE_REG;
- reg_e = mr->dist_reg;
- reg_d = mr->dist_reg;
-
- fn += (mr->reg_width >> 3) - 1;
- return _INTC_MK(fn, mode,
- intc_get_reg(d, reg_e),
- intc_get_reg(d, reg_d),
- 1,
- (mr->reg_width - 1) - j);
- }
- }
-
- /*
- * It's possible we've gotten here with no distribution options
- * available for the IRQ in question, so we just skip over those.
- */
- return 0;
-}
-#else
-static inline void intc_balancing_enable(unsigned int irq)
-{
-}
-
-static inline void intc_balancing_disable(unsigned int irq)
-{
-}
-#endif
-
-static inline void _intc_enable(unsigned int irq, unsigned long handle)
-{
- struct intc_desc_int *d = get_intc_desc(irq);
- unsigned long addr;
- unsigned int cpu;
-
- for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
-#ifdef CONFIG_SMP
- if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
- continue;
-#endif
- addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
- intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
- [_INTC_FN(handle)], irq);
- }
-
- intc_balancing_enable(irq);
-}
-
-static void intc_enable(unsigned int irq)
-{
- _intc_enable(irq, (unsigned long)get_irq_chip_data(irq));
-}
-
-static void intc_disable(unsigned int irq)
-{
- struct intc_desc_int *d = get_intc_desc(irq);
- unsigned long handle = (unsigned long)get_irq_chip_data(irq);
- unsigned long addr;
- unsigned int cpu;
-
- intc_balancing_disable(irq);
-
- for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
-#ifdef CONFIG_SMP
- if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
- continue;
-#endif
- addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
- intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
- [_INTC_FN(handle)], irq);
- }
-}
-
-static void (*intc_enable_noprio_fns[])(unsigned long addr,
- unsigned long handle,
- void (*fn)(unsigned long,
- unsigned long,
- unsigned long),
- unsigned int irq) = {
- [MODE_ENABLE_REG] = intc_mode_field,
- [MODE_MASK_REG] = intc_mode_zero,
- [MODE_DUAL_REG] = intc_mode_field,
- [MODE_PRIO_REG] = intc_mode_field,
- [MODE_PCLR_REG] = intc_mode_field,
-};
-
-static void intc_enable_disable(struct intc_desc_int *d,
- unsigned long handle, int do_enable)
-{
- unsigned long addr;
- unsigned int cpu;
- void (*fn)(unsigned long, unsigned long,
- void (*)(unsigned long, unsigned long, unsigned long),
- unsigned int);
-
- if (do_enable) {
- for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
- addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
- fn = intc_enable_noprio_fns[_INTC_MODE(handle)];
- fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
- }
- } else {
- for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
- addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
- fn = intc_disable_fns[_INTC_MODE(handle)];
- fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
- }
- }
-}
-
-static int intc_set_wake(unsigned int irq, unsigned int on)
-{
- return 0; /* allow wakeup, but setup hardware in intc_suspend() */
-}
-
-#ifdef CONFIG_SMP
-/*
- * This is held with the irq desc lock held, so we don't require any
- * additional locking here at the intc desc level. The affinity mask is
- * later tested in the enable/disable paths.
- */
-static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask)
-{
- if (!cpumask_intersects(cpumask, cpu_online_mask))
- return -1;
-
- cpumask_copy(irq_to_desc(irq)->affinity, cpumask);
-
- return 0;
-}
-#endif
-
-static void intc_mask_ack(unsigned int irq)
-{
- struct intc_desc_int *d = get_intc_desc(irq);
- unsigned long handle = ack_handle[irq];
- unsigned long addr;
-
- intc_disable(irq);
-
- /* read register and write zero only to the associated bit */
- if (handle) {
- addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
- switch (_INTC_FN(handle)) {
- case REG_FN_MODIFY_BASE + 0: /* 8bit */
- __raw_readb(addr);
- __raw_writeb(0xff ^ set_field(0, 1, handle), addr);
- break;
- case REG_FN_MODIFY_BASE + 1: /* 16bit */
- __raw_readw(addr);
- __raw_writew(0xffff ^ set_field(0, 1, handle), addr);
- break;
- case REG_FN_MODIFY_BASE + 3: /* 32bit */
- __raw_readl(addr);
- __raw_writel(0xffffffff ^ set_field(0, 1, handle), addr);
- break;
- default:
- BUG();
- break;
- }
- }
-}
-
-static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
- unsigned int nr_hp,
- unsigned int irq)
-{
- int i;
-
- /*
- * this doesn't scale well, but...
- *
- * this function should only be used for cerain uncommon
- * operations such as intc_set_priority() and intc_set_sense()
- * and in those rare cases performance doesn't matter that much.
- * keeping the memory footprint low is more important.
- *
- * one rather simple way to speed this up and still keep the
- * memory footprint down is to make sure the array is sorted
- * and then perform a bisect to lookup the irq.
- */
- for (i = 0; i < nr_hp; i++) {
- if ((hp + i)->irq != irq)
- continue;
-
- return hp + i;
- }
-
- return NULL;
-}
-
-int intc_set_priority(unsigned int irq, unsigned int prio)
-{
- struct intc_desc_int *d = get_intc_desc(irq);
- struct intc_handle_int *ihp;
-
- if (!intc_prio_level[irq] || prio <= 1)
- return -EINVAL;
-
- ihp = intc_find_irq(d->prio, d->nr_prio, irq);
- if (ihp) {
- if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
- return -EINVAL;
-
- intc_prio_level[irq] = prio;
-
- /*
- * only set secondary masking method directly
- * primary masking method is using intc_prio_level[irq]
- * priority level will be set during next enable()
- */
- if (_INTC_FN(ihp->handle) != REG_FN_ERR)
- _intc_enable(irq, ihp->handle);
- }
- return 0;
-}
-
-#define VALID(x) (x | 0x80)
-
-static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
- [IRQ_TYPE_EDGE_FALLING] = VALID(0),
- [IRQ_TYPE_EDGE_RISING] = VALID(1),
- [IRQ_TYPE_LEVEL_LOW] = VALID(2),
- /* SH7706, SH7707 and SH7709 do not support high level triggered */
-#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
- !defined(CONFIG_CPU_SUBTYPE_SH7707) && \
- !defined(CONFIG_CPU_SUBTYPE_SH7709)
- [IRQ_TYPE_LEVEL_HIGH] = VALID(3),
-#endif
-};
-
-static int intc_set_sense(unsigned int irq, unsigned int type)
-{
- struct intc_desc_int *d = get_intc_desc(irq);
- unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
- struct intc_handle_int *ihp;
- unsigned long addr;
-
- if (!value)
- return -EINVAL;
-
- ihp = intc_find_irq(d->sense, d->nr_sense, irq);
- if (ihp) {
- addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
- intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
- }
- return 0;
-}
-
-static intc_enum __init intc_grp_id(struct intc_desc *desc,
- intc_enum enum_id)
-{
- struct intc_group *g = desc->hw.groups;
- unsigned int i, j;
-
- for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) {
- g = desc->hw.groups + i;
-
- for (j = 0; g->enum_ids[j]; j++) {
- if (g->enum_ids[j] != enum_id)
- continue;
-
- return g->enum_id;
- }
- }
-
- return 0;
-}
-
-static unsigned int __init _intc_mask_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id,
- unsigned int *reg_idx,
- unsigned int *fld_idx)
-{
- struct intc_mask_reg *mr = desc->hw.mask_regs;
- unsigned int fn, mode;
- unsigned long reg_e, reg_d;
-
- while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) {
- mr = desc->hw.mask_regs + *reg_idx;
-
- for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) {
- if (mr->enum_ids[*fld_idx] != enum_id)
- continue;
-
- if (mr->set_reg && mr->clr_reg) {
- fn = REG_FN_WRITE_BASE;
- mode = MODE_DUAL_REG;
- reg_e = mr->clr_reg;
- reg_d = mr->set_reg;
- } else {
- fn = REG_FN_MODIFY_BASE;
- if (mr->set_reg) {
- mode = MODE_ENABLE_REG;
- reg_e = mr->set_reg;
- reg_d = mr->set_reg;
- } else {
- mode = MODE_MASK_REG;
- reg_e = mr->clr_reg;
- reg_d = mr->clr_reg;
- }
- }
-
- fn += (mr->reg_width >> 3) - 1;
- return _INTC_MK(fn, mode,
- intc_get_reg(d, reg_e),
- intc_get_reg(d, reg_d),
- 1,
- (mr->reg_width - 1) - *fld_idx);
- }
-
- *fld_idx = 0;
- (*reg_idx)++;
- }
-
- return 0;
-}
-
-static unsigned int __init intc_mask_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id, int do_grps)
-{
- unsigned int i = 0;
- unsigned int j = 0;
- unsigned int ret;
-
- ret = _intc_mask_data(desc, d, enum_id, &i, &j);
- if (ret)
- return ret;
-
- if (do_grps)
- return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0);
-
- return 0;
-}
-
-static unsigned int __init _intc_prio_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id,
- unsigned int *reg_idx,
- unsigned int *fld_idx)
-{
- struct intc_prio_reg *pr = desc->hw.prio_regs;
- unsigned int fn, n, mode, bit;
- unsigned long reg_e, reg_d;
-
- while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) {
- pr = desc->hw.prio_regs + *reg_idx;
-
- for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) {
- if (pr->enum_ids[*fld_idx] != enum_id)
- continue;
-
- if (pr->set_reg && pr->clr_reg) {
- fn = REG_FN_WRITE_BASE;
- mode = MODE_PCLR_REG;
- reg_e = pr->set_reg;
- reg_d = pr->clr_reg;
- } else {
- fn = REG_FN_MODIFY_BASE;
- mode = MODE_PRIO_REG;
- if (!pr->set_reg)
- BUG();
- reg_e = pr->set_reg;
- reg_d = pr->set_reg;
- }
-
- fn += (pr->reg_width >> 3) - 1;
- n = *fld_idx + 1;
-
- BUG_ON(n * pr->field_width > pr->reg_width);
-
- bit = pr->reg_width - (n * pr->field_width);
-
- return _INTC_MK(fn, mode,
- intc_get_reg(d, reg_e),
- intc_get_reg(d, reg_d),
- pr->field_width, bit);
- }
-
- *fld_idx = 0;
- (*reg_idx)++;
- }
-
- return 0;
-}
-
-static unsigned int __init intc_prio_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id, int do_grps)
-{
- unsigned int i = 0;
- unsigned int j = 0;
- unsigned int ret;
-
- ret = _intc_prio_data(desc, d, enum_id, &i, &j);
- if (ret)
- return ret;
-
- if (do_grps)
- return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0);
-
- return 0;
-}
-
-static void __init intc_enable_disable_enum(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id, int enable)
-{
- unsigned int i, j, data;
-
- /* go through and enable/disable all mask bits */
- i = j = 0;
- do {
- data = _intc_mask_data(desc, d, enum_id, &i, &j);
- if (data)
- intc_enable_disable(d, data, enable);
- j++;
- } while (data);
-
- /* go through and enable/disable all priority fields */
- i = j = 0;
- do {
- data = _intc_prio_data(desc, d, enum_id, &i, &j);
- if (data)
- intc_enable_disable(d, data, enable);
-
- j++;
- } while (data);
-}
-
-static unsigned int __init intc_ack_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id)
-{
- struct intc_mask_reg *mr = desc->hw.ack_regs;
- unsigned int i, j, fn, mode;
- unsigned long reg_e, reg_d;
-
- for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) {
- mr = desc->hw.ack_regs + i;
-
- for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
- if (mr->enum_ids[j] != enum_id)
- continue;
-
- fn = REG_FN_MODIFY_BASE;
- mode = MODE_ENABLE_REG;
- reg_e = mr->set_reg;
- reg_d = mr->set_reg;
-
- fn += (mr->reg_width >> 3) - 1;
- return _INTC_MK(fn, mode,
- intc_get_reg(d, reg_e),
- intc_get_reg(d, reg_d),
- 1,
- (mr->reg_width - 1) - j);
- }
- }
-
- return 0;
-}
-
-static unsigned int __init intc_sense_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id)
-{
- struct intc_sense_reg *sr = desc->hw.sense_regs;
- unsigned int i, j, fn, bit;
-
- for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) {
- sr = desc->hw.sense_regs + i;
-
- for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) {
- if (sr->enum_ids[j] != enum_id)
- continue;
-
- fn = REG_FN_MODIFY_BASE;
- fn += (sr->reg_width >> 3) - 1;
-
- BUG_ON((j + 1) * sr->field_width > sr->reg_width);
-
- bit = sr->reg_width - ((j + 1) * sr->field_width);
-
- return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg),
- 0, sr->field_width, bit);
- }
- }
-
- return 0;
-}
-
-static void __init intc_register_irq(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id,
- unsigned int irq)
-{
- struct intc_handle_int *hp;
- unsigned int data[2], primary;
-
- /*
- * Register the IRQ position with the global IRQ map
- */
- set_bit(irq, intc_irq_map);
-
- /*
- * Prefer single interrupt source bitmap over other combinations:
- *
- * 1. bitmap, single interrupt source
- * 2. priority, single interrupt source
- * 3. bitmap, multiple interrupt sources (groups)
- * 4. priority, multiple interrupt sources (groups)
- */
- data[0] = intc_mask_data(desc, d, enum_id, 0);
- data[1] = intc_prio_data(desc, d, enum_id, 0);
-
- primary = 0;
- if (!data[0] && data[1])
- primary = 1;
-
- if (!data[0] && !data[1])
- pr_warning("missing unique irq mask for irq %d (vect 0x%04x)\n",
- irq, irq2evt(irq));
-
- data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1);
- data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1);
-
- if (!data[primary])
- primary ^= 1;
-
- BUG_ON(!data[primary]); /* must have primary masking method */
-
- disable_irq_nosync(irq);
- set_irq_chip_and_handler_name(irq, &d->chip,
- handle_level_irq, "level");
- set_irq_chip_data(irq, (void *)data[primary]);
-
- /*
- * set priority level
- * - this needs to be at least 2 for 5-bit priorities on 7780
- */
- intc_prio_level[irq] = default_prio_level;
-
- /* enable secondary masking method if present */
- if (data[!primary])
- _intc_enable(irq, data[!primary]);
-
- /* add irq to d->prio list if priority is available */
- if (data[1]) {
- hp = d->prio + d->nr_prio;
- hp->irq = irq;
- hp->handle = data[1];
-
- if (primary) {
- /*
- * only secondary priority should access registers, so
- * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority()
- */
- hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0);
- hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0);
- }
- d->nr_prio++;
- }
-
- /* add irq to d->sense list if sense is available */
- data[0] = intc_sense_data(desc, d, enum_id);
- if (data[0]) {
- (d->sense + d->nr_sense)->irq = irq;
- (d->sense + d->nr_sense)->handle = data[0];
- d->nr_sense++;
- }
-
- /* irq should be disabled by default */
- d->chip.mask(irq);
-
- if (desc->hw.ack_regs)
- ack_handle[irq] = intc_ack_data(desc, d, enum_id);
-
-#ifdef CONFIG_INTC_BALANCING
- if (desc->hw.mask_regs)
- dist_handle[irq] = intc_dist_data(desc, d, enum_id);
-#endif
-
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */
-#endif
-}
-
-static unsigned int __init save_reg(struct intc_desc_int *d,
- unsigned int cnt,
- unsigned long value,
- unsigned int smp)
-{
- if (value) {
- value = intc_phys_to_virt(d, value);
-
- d->reg[cnt] = value;
-#ifdef CONFIG_SMP
- d->smp[cnt] = smp;
-#endif
- return 1;
- }
-
- return 0;
-}
-
-static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
-{
- generic_handle_irq((unsigned int)get_irq_data(irq));
-}
-
-int __init register_intc_controller(struct intc_desc *desc)
-{
- unsigned int i, k, smp;
- struct intc_hw_desc *hw = &desc->hw;
- struct intc_desc_int *d;
- struct resource *res;
-
- pr_info("Registered controller '%s' with %u IRQs\n",
- desc->name, hw->nr_vectors);
-
- d = kzalloc(sizeof(*d), GFP_NOWAIT);
- if (!d)
- goto err0;
-
- INIT_LIST_HEAD(&d->list);
- list_add(&d->list, &intc_list);
-
- if (desc->num_resources) {
- d->nr_windows = desc->num_resources;
- d->window = kzalloc(d->nr_windows * sizeof(*d->window),
- GFP_NOWAIT);
- if (!d->window)
- goto err1;
-
- for (k = 0; k < d->nr_windows; k++) {
- res = desc->resource + k;
- WARN_ON(resource_type(res) != IORESOURCE_MEM);
- d->window[k].phys = res->start;
- d->window[k].size = resource_size(res);
- d->window[k].virt = ioremap_nocache(res->start,
- resource_size(res));
- if (!d->window[k].virt)
- goto err2;
- }
- }
-
- d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
-#ifdef CONFIG_INTC_BALANCING
- if (d->nr_reg)
- d->nr_reg += hw->nr_mask_regs;
-#endif
- d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
- d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
- d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
-
- d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
- if (!d->reg)
- goto err2;
-
-#ifdef CONFIG_SMP
- d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
- if (!d->smp)
- goto err3;
-#endif
- k = 0;
-
- if (hw->mask_regs) {
- for (i = 0; i < hw->nr_mask_regs; i++) {
- smp = IS_SMP(hw->mask_regs[i]);
- k += save_reg(d, k, hw->mask_regs[i].set_reg, smp);
- k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp);
-#ifdef CONFIG_INTC_BALANCING
- k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0);
-#endif
- }
- }
-
- if (hw->prio_regs) {
- d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio),
- GFP_NOWAIT);
- if (!d->prio)
- goto err4;
-
- for (i = 0; i < hw->nr_prio_regs; i++) {
- smp = IS_SMP(hw->prio_regs[i]);
- k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
- k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
- }
- }
-
- if (hw->sense_regs) {
- d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense),
- GFP_NOWAIT);
- if (!d->sense)
- goto err5;
-
- for (i = 0; i < hw->nr_sense_regs; i++)
- k += save_reg(d, k, hw->sense_regs[i].reg, 0);
- }
-
- d->chip.name = desc->name;
- d->chip.mask = intc_disable;
- d->chip.unmask = intc_enable;
- d->chip.mask_ack = intc_disable;
- d->chip.enable = intc_enable;
- d->chip.disable = intc_disable;
- d->chip.shutdown = intc_disable;
- d->chip.set_type = intc_set_sense;
- d->chip.set_wake = intc_set_wake;
-#ifdef CONFIG_SMP
- d->chip.set_affinity = intc_set_affinity;
-#endif
-
- if (hw->ack_regs) {
- for (i = 0; i < hw->nr_ack_regs; i++)
- k += save_reg(d, k, hw->ack_regs[i].set_reg, 0);
-
- d->chip.mask_ack = intc_mask_ack;
- }
-
- /* disable bits matching force_disable before registering irqs */
- if (desc->force_disable)
- intc_enable_disable_enum(desc, d, desc->force_disable, 0);
-
- /* disable bits matching force_enable before registering irqs */
- if (desc->force_enable)
- intc_enable_disable_enum(desc, d, desc->force_enable, 0);
-
- BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
-
- /* register the vectors one by one */
- for (i = 0; i < hw->nr_vectors; i++) {
- struct intc_vect *vect = hw->vectors + i;
- unsigned int irq = evt2irq(vect->vect);
- struct irq_desc *irq_desc;
-
- if (!vect->enum_id)
- continue;
-
- irq_desc = irq_to_desc_alloc_node(irq, numa_node_id());
- if (unlikely(!irq_desc)) {
- pr_err("can't get irq_desc for %d\n", irq);
- continue;
- }
-
- intc_register_irq(desc, d, vect->enum_id, irq);
-
- for (k = i + 1; k < hw->nr_vectors; k++) {
- struct intc_vect *vect2 = hw->vectors + k;
- unsigned int irq2 = evt2irq(vect2->vect);
-
- if (vect->enum_id != vect2->enum_id)
- continue;
-
- /*
- * In the case of multi-evt handling and sparse
- * IRQ support, each vector still needs to have
- * its own backing irq_desc.
- */
- irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id());
- if (unlikely(!irq_desc)) {
- pr_err("can't get irq_desc for %d\n", irq2);
- continue;
- }
-
- vect2->enum_id = 0;
-
- /* redirect this interrupts to the first one */
- set_irq_chip(irq2, &dummy_irq_chip);
- set_irq_chained_handler(irq2, intc_redirect_irq);
- set_irq_data(irq2, (void *)irq);
- }
- }
-
- /* enable bits matching force_enable after registering irqs */
- if (desc->force_enable)
- intc_enable_disable_enum(desc, d, desc->force_enable, 1);
-
- return 0;
-err5:
- kfree(d->prio);
-err4:
-#ifdef CONFIG_SMP
- kfree(d->smp);
-err3:
-#endif
- kfree(d->reg);
-err2:
- for (k = 0; k < d->nr_windows; k++)
- if (d->window[k].virt)
- iounmap(d->window[k].virt);
-
- kfree(d->window);
-err1:
- kfree(d);
-err0:
- pr_err("unable to allocate INTC memory\n");
-
- return -ENOMEM;
-}
-
-#ifdef CONFIG_INTC_USERIMASK
-static void __iomem *uimask;
-
-int register_intc_userimask(unsigned long addr)
-{
- if (unlikely(uimask))
- return -EBUSY;
-
- uimask = ioremap_nocache(addr, SZ_4K);
- if (unlikely(!uimask))
- return -ENOMEM;
-
- pr_info("userimask support registered for levels 0 -> %d\n",
- default_prio_level - 1);
-
- return 0;
-}
-
-static ssize_t
-show_intc_userimask(struct sysdev_class *cls,
- struct sysdev_class_attribute *attr, char *buf)
-{
- return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);
-}
-
-static ssize_t
-store_intc_userimask(struct sysdev_class *cls,
- struct sysdev_class_attribute *attr,
- const char *buf, size_t count)
-{
- unsigned long level;
-
- level = simple_strtoul(buf, NULL, 10);
-
- /*
- * Minimal acceptable IRQ levels are in the 2 - 16 range, but
- * these are chomped so as to not interfere with normal IRQs.
- *
- * Level 1 is a special case on some CPUs in that it's not
- * directly settable, but given that USERIMASK cuts off below a
- * certain level, we don't care about this limitation here.
- * Level 0 on the other hand equates to user masking disabled.
- *
- * We use default_prio_level as a cut off so that only special
- * case opt-in IRQs can be mangled.
- */
- if (level >= default_prio_level)
- return -EINVAL;
-
- __raw_writel(0xa5 << 24 | level << 4, uimask);
-
- return count;
-}
-
-static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR,
- show_intc_userimask, store_intc_userimask);
-#endif
-
-static ssize_t
-show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
-{
- struct intc_desc_int *d;
-
- d = container_of(dev, struct intc_desc_int, sysdev);
-
- return sprintf(buf, "%s\n", d->chip.name);
-}
-
-static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL);
-
-static int intc_suspend(struct sys_device *dev, pm_message_t state)
-{
- struct intc_desc_int *d;
- struct irq_desc *desc;
- int irq;
-
- /* get intc controller associated with this sysdev */
- d = container_of(dev, struct intc_desc_int, sysdev);
-
- switch (state.event) {
- case PM_EVENT_ON:
- if (d->state.event != PM_EVENT_FREEZE)
- break;
- for_each_irq_desc(irq, desc) {
- if (desc->handle_irq == intc_redirect_irq)
- continue;
- if (desc->chip != &d->chip)
- continue;
- if (desc->status & IRQ_DISABLED)
- intc_disable(irq);
- else
- intc_enable(irq);
- }
- break;
- case PM_EVENT_FREEZE:
- /* nothing has to be done */
- break;
- case PM_EVENT_SUSPEND:
- /* enable wakeup irqs belonging to this intc controller */
- for_each_irq_desc(irq, desc) {
- if ((desc->status & IRQ_WAKEUP) && (desc->chip == &d->chip))
- intc_enable(irq);
- }
- break;
- }
- d->state = state;
-
- return 0;
-}
-
-static int intc_resume(struct sys_device *dev)
-{
- return intc_suspend(dev, PMSG_ON);
-}
-
-static struct sysdev_class intc_sysdev_class = {
- .name = "intc",
- .suspend = intc_suspend,
- .resume = intc_resume,
-};
-
-/* register this intc as sysdev to allow suspend/resume */
-static int __init register_intc_sysdevs(void)
-{
- struct intc_desc_int *d;
- int error;
- int id = 0;
-
- error = sysdev_class_register(&intc_sysdev_class);
-#ifdef CONFIG_INTC_USERIMASK
- if (!error && uimask)
- error = sysdev_class_create_file(&intc_sysdev_class,
- &attr_userimask);
-#endif
- if (!error) {
- list_for_each_entry(d, &intc_list, list) {
- d->sysdev.id = id;
- d->sysdev.cls = &intc_sysdev_class;
- error = sysdev_register(&d->sysdev);
- if (error == 0)
- error = sysdev_create_file(&d->sysdev,
- &attr_name);
- if (error)
- break;
-
- id++;
- }
- }
-
- if (error)
- pr_err("sysdev registration error\n");
-
- return error;
-}
-device_initcall(register_intc_sysdevs);
-
-/*
- * Dynamic IRQ allocation and deallocation
- */
-unsigned int create_irq_nr(unsigned int irq_want, int node)
-{
- unsigned int irq = 0, new;
- unsigned long flags;
- struct irq_desc *desc;
-
- spin_lock_irqsave(&vector_lock, flags);
-
- /*
- * First try the wanted IRQ
- */
- if (test_and_set_bit(irq_want, intc_irq_map) == 0) {
- new = irq_want;
- } else {
- /* .. then fall back to scanning. */
- new = find_first_zero_bit(intc_irq_map, nr_irqs);
- if (unlikely(new == nr_irqs))
- goto out_unlock;
-
- __set_bit(new, intc_irq_map);
- }
-
- desc = irq_to_desc_alloc_node(new, node);
- if (unlikely(!desc)) {
- pr_err("can't get irq_desc for %d\n", new);
- goto out_unlock;
- }
-
- desc = move_irq_desc(desc, node);
- irq = new;
-
-out_unlock:
- spin_unlock_irqrestore(&vector_lock, flags);
-
- if (irq > 0) {
- dynamic_irq_init(irq);
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */
-#endif
- }
-
- return irq;
-}
-
-int create_irq(void)
-{
- int nid = cpu_to_node(smp_processor_id());
- int irq;
-
- irq = create_irq_nr(NR_IRQS_LEGACY, nid);
- if (irq == 0)
- irq = -1;
-
- return irq;
-}
-
-void destroy_irq(unsigned int irq)
-{
- unsigned long flags;
-
- dynamic_irq_cleanup(irq);
-
- spin_lock_irqsave(&vector_lock, flags);
- __clear_bit(irq, intc_irq_map);
- spin_unlock_irqrestore(&vector_lock, flags);
-}
-
-int reserve_irq_vector(unsigned int irq)
-{
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&vector_lock, flags);
- if (test_and_set_bit(irq, intc_irq_map))
- ret = -EBUSY;
- spin_unlock_irqrestore(&vector_lock, flags);
-
- return ret;
-}
-
-void reserve_irq_legacy(void)
-{
- unsigned long flags;
- int i, j;
-
- spin_lock_irqsave(&vector_lock, flags);
- j = find_first_bit(intc_irq_map, nr_irqs);
- for (i = 0; i < j; i++)
- __set_bit(i, intc_irq_map);
- spin_unlock_irqrestore(&vector_lock, flags);
-}
diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig
new file mode 100644
index 000000000000..c88cbccc62b0
--- /dev/null
+++ b/drivers/sh/intc/Kconfig
@@ -0,0 +1,35 @@
+comment "Interrupt controller options"
+
+config INTC_USERIMASK
+ bool "Userspace interrupt masking support"
+ depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
+ help
+ This enables support for hardware-assisted userspace hardirq
+ masking.
+
+ SH-4A and newer interrupt blocks all support a special shadowed
+ page with all non-masking registers obscured when mapped in to
+ userspace. This is primarily for use by userspace device
+ drivers that are using special priority levels.
+
+ If in doubt, say N.
+
+config INTC_BALANCING
+ bool "Hardware IRQ balancing support"
+ depends on SMP && SUPERH && CPU_SHX3
+ help
+ This enables support for IRQ auto-distribution mode on SH-X3
+ SMP parts. All of the balancing and CPU wakeup decisions are
+ taken care of automatically by hardware for distributed
+ vectors.
+
+ If in doubt, say N.
+
+config INTC_MAPPING_DEBUG
+ bool "Expose IRQ to per-controller id mapping via debugfs"
+ depends on DEBUG_FS
+ help
+ This will create a debugfs entry for showing the relationship
+ between system IRQs and the per-controller id tables.
+
+ If in doubt, say N.
diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile
new file mode 100644
index 000000000000..bb5df868d77a
--- /dev/null
+++ b/drivers/sh/intc/Makefile
@@ -0,0 +1,5 @@
+obj-y := access.o chip.o core.o dynamic.o handle.o virq.o
+
+obj-$(CONFIG_INTC_BALANCING) += balancing.o
+obj-$(CONFIG_INTC_USERIMASK) += userimask.o
+obj-$(CONFIG_INTC_MAPPING_DEBUG) += virq-debugfs.o
diff --git a/drivers/sh/intc/access.c b/drivers/sh/intc/access.c
new file mode 100644
index 000000000000..f892ae1d212a
--- /dev/null
+++ b/drivers/sh/intc/access.c
@@ -0,0 +1,237 @@
+/*
+ * Common INTC2 register accessors
+ *
+ * Copyright (C) 2007, 2008 Magnus Damm
+ * Copyright (C) 2009, 2010 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/io.h>
+#include "internals.h"
+
+unsigned long intc_phys_to_virt(struct intc_desc_int *d, unsigned long address)
+{
+ struct intc_window *window;
+ int k;
+
+ /* scan through physical windows and convert address */
+ for (k = 0; k < d->nr_windows; k++) {
+ window = d->window + k;
+
+ if (address < window->phys)
+ continue;
+
+ if (address >= (window->phys + window->size))
+ continue;
+
+ address -= window->phys;
+ address += (unsigned long)window->virt;
+
+ return address;
+ }
+
+ /* no windows defined, register must be 1:1 mapped virt:phys */
+ return address;
+}
+
+unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address)
+{
+ unsigned int k;
+
+ address = intc_phys_to_virt(d, address);
+
+ for (k = 0; k < d->nr_reg; k++) {
+ if (d->reg[k] == address)
+ return k;
+ }
+
+ BUG();
+ return 0;
+}
+
+unsigned int intc_set_field_from_handle(unsigned int value,
+ unsigned int field_value,
+ unsigned int handle)
+{
+ unsigned int width = _INTC_WIDTH(handle);
+ unsigned int shift = _INTC_SHIFT(handle);
+
+ value &= ~(((1 << width) - 1) << shift);
+ value |= field_value << shift;
+ return value;
+}
+
+unsigned long intc_get_field_from_handle(unsigned int value, unsigned int handle)
+{
+ unsigned int width = _INTC_WIDTH(handle);
+ unsigned int shift = _INTC_SHIFT(handle);
+ unsigned int mask = ((1 << width) - 1) << shift;
+
+ return (value & mask) >> shift;
+}
+
+static unsigned long test_8(unsigned long addr, unsigned long h,
+ unsigned long ignore)
+{
+ return intc_get_field_from_handle(__raw_readb(addr), h);
+}
+
+static unsigned long test_16(unsigned long addr, unsigned long h,
+ unsigned long ignore)
+{
+ return intc_get_field_from_handle(__raw_readw(addr), h);
+}
+
+static unsigned long test_32(unsigned long addr, unsigned long h,
+ unsigned long ignore)
+{
+ return intc_get_field_from_handle(__raw_readl(addr), h);
+}
+
+static unsigned long write_8(unsigned long addr, unsigned long h,
+ unsigned long data)
+{
+ __raw_writeb(intc_set_field_from_handle(0, data, h), addr);
+ (void)__raw_readb(addr); /* Defeat write posting */
+ return 0;
+}
+
+static unsigned long write_16(unsigned long addr, unsigned long h,
+ unsigned long data)
+{
+ __raw_writew(intc_set_field_from_handle(0, data, h), addr);
+ (void)__raw_readw(addr); /* Defeat write posting */
+ return 0;
+}
+
+static unsigned long write_32(unsigned long addr, unsigned long h,
+ unsigned long data)
+{
+ __raw_writel(intc_set_field_from_handle(0, data, h), addr);
+ (void)__raw_readl(addr); /* Defeat write posting */
+ return 0;
+}
+
+static unsigned long modify_8(unsigned long addr, unsigned long h,
+ unsigned long data)
+{
+ unsigned long flags;
+ unsigned int value;
+ local_irq_save(flags);
+ value = intc_set_field_from_handle(__raw_readb(addr), data, h);
+ __raw_writeb(value, addr);
+ (void)__raw_readb(addr); /* Defeat write posting */
+ local_irq_restore(flags);
+ return 0;
+}
+
+static unsigned long modify_16(unsigned long addr, unsigned long h,
+ unsigned long data)
+{
+ unsigned long flags;
+ unsigned int value;
+ local_irq_save(flags);
+ value = intc_set_field_from_handle(__raw_readw(addr), data, h);
+ __raw_writew(value, addr);
+ (void)__raw_readw(addr); /* Defeat write posting */
+ local_irq_restore(flags);
+ return 0;
+}
+
+static unsigned long modify_32(unsigned long addr, unsigned long h,
+ unsigned long data)
+{
+ unsigned long flags;
+ unsigned int value;
+ local_irq_save(flags);
+ value = intc_set_field_from_handle(__raw_readl(addr), data, h);
+ __raw_writel(value, addr);
+ (void)__raw_readl(addr); /* Defeat write posting */
+ local_irq_restore(flags);
+ return 0;
+}
+
+static unsigned long intc_mode_field(unsigned long addr,
+ unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long,
+ unsigned long),
+ unsigned int irq)
+{
+ return fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
+}
+
+static unsigned long intc_mode_zero(unsigned long addr,
+ unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long,
+ unsigned long),
+ unsigned int irq)
+{
+ return fn(addr, handle, 0);
+}
+
+static unsigned long intc_mode_prio(unsigned long addr,
+ unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long,
+ unsigned long),
+ unsigned int irq)
+{
+ return fn(addr, handle, intc_get_prio_level(irq));
+}
+
+unsigned long (*intc_reg_fns[])(unsigned long addr,
+ unsigned long h,
+ unsigned long data) = {
+ [REG_FN_TEST_BASE + 0] = test_8,
+ [REG_FN_TEST_BASE + 1] = test_16,
+ [REG_FN_TEST_BASE + 3] = test_32,
+ [REG_FN_WRITE_BASE + 0] = write_8,
+ [REG_FN_WRITE_BASE + 1] = write_16,
+ [REG_FN_WRITE_BASE + 3] = write_32,
+ [REG_FN_MODIFY_BASE + 0] = modify_8,
+ [REG_FN_MODIFY_BASE + 1] = modify_16,
+ [REG_FN_MODIFY_BASE + 3] = modify_32,
+};
+
+unsigned long (*intc_enable_fns[])(unsigned long addr,
+ unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long,
+ unsigned long),
+ unsigned int irq) = {
+ [MODE_ENABLE_REG] = intc_mode_field,
+ [MODE_MASK_REG] = intc_mode_zero,
+ [MODE_DUAL_REG] = intc_mode_field,
+ [MODE_PRIO_REG] = intc_mode_prio,
+ [MODE_PCLR_REG] = intc_mode_prio,
+};
+
+unsigned long (*intc_disable_fns[])(unsigned long addr,
+ unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long,
+ unsigned long),
+ unsigned int irq) = {
+ [MODE_ENABLE_REG] = intc_mode_zero,
+ [MODE_MASK_REG] = intc_mode_field,
+ [MODE_DUAL_REG] = intc_mode_field,
+ [MODE_PRIO_REG] = intc_mode_zero,
+ [MODE_PCLR_REG] = intc_mode_field,
+};
+
+unsigned long (*intc_enable_noprio_fns[])(unsigned long addr,
+ unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long,
+ unsigned long),
+ unsigned int irq) = {
+ [MODE_ENABLE_REG] = intc_mode_field,
+ [MODE_MASK_REG] = intc_mode_zero,
+ [MODE_DUAL_REG] = intc_mode_field,
+ [MODE_PRIO_REG] = intc_mode_field,
+ [MODE_PCLR_REG] = intc_mode_field,
+};
diff --git a/drivers/sh/intc/balancing.c b/drivers/sh/intc/balancing.c
new file mode 100644
index 000000000000..cec7a96f2c09
--- /dev/null
+++ b/drivers/sh/intc/balancing.c
@@ -0,0 +1,97 @@
+/*
+ * Support for hardware-managed IRQ auto-distribution.
+ *
+ * Copyright (C) 2010 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include "internals.h"
+
+static unsigned long dist_handle[NR_IRQS];
+
+void intc_balancing_enable(unsigned int irq)
+{
+ struct intc_desc_int *d = get_intc_desc(irq);
+ unsigned long handle = dist_handle[irq];
+ unsigned long addr;
+
+ if (irq_balancing_disabled(irq) || !handle)
+ return;
+
+ addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
+ intc_reg_fns[_INTC_FN(handle)](addr, handle, 1);
+}
+
+void intc_balancing_disable(unsigned int irq)
+{
+ struct intc_desc_int *d = get_intc_desc(irq);
+ unsigned long handle = dist_handle[irq];
+ unsigned long addr;
+
+ if (irq_balancing_disabled(irq) || !handle)
+ return;
+
+ addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
+ intc_reg_fns[_INTC_FN(handle)](addr, handle, 0);
+}
+
+static unsigned int intc_dist_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id)
+{
+ struct intc_mask_reg *mr = desc->hw.mask_regs;
+ unsigned int i, j, fn, mode;
+ unsigned long reg_e, reg_d;
+
+ for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) {
+ mr = desc->hw.mask_regs + i;
+
+ /*
+ * Skip this entry if there's no auto-distribution
+ * register associated with it.
+ */
+ if (!mr->dist_reg)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
+ if (mr->enum_ids[j] != enum_id)
+ continue;
+
+ fn = REG_FN_MODIFY_BASE;
+ mode = MODE_ENABLE_REG;
+ reg_e = mr->dist_reg;
+ reg_d = mr->dist_reg;
+
+ fn += (mr->reg_width >> 3) - 1;
+ return _INTC_MK(fn, mode,
+ intc_get_reg(d, reg_e),
+ intc_get_reg(d, reg_d),
+ 1,
+ (mr->reg_width - 1) - j);
+ }
+ }
+
+ /*
+ * It's possible we've gotten here with no distribution options
+ * available for the IRQ in question, so we just skip over those.
+ */
+ return 0;
+}
+
+void intc_set_dist_handle(unsigned int irq, struct intc_desc *desc,
+ struct intc_desc_int *d, intc_enum id)
+{
+ unsigned long flags;
+
+ /*
+ * Nothing to do for this IRQ.
+ */
+ if (!desc->hw.mask_regs)
+ return;
+
+ raw_spin_lock_irqsave(&intc_big_lock, flags);
+ dist_handle[irq] = intc_dist_data(desc, d, id);
+ raw_spin_unlock_irqrestore(&intc_big_lock, flags);
+}
diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c
new file mode 100644
index 000000000000..35c03706cc21
--- /dev/null
+++ b/drivers/sh/intc/chip.c
@@ -0,0 +1,215 @@
+/*
+ * IRQ chip definitions for INTC IRQs.
+ *
+ * Copyright (C) 2007, 2008 Magnus Damm
+ * Copyright (C) 2009, 2010 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/cpumask.h>
+#include <linux/io.h>
+#include "internals.h"
+
+void _intc_enable(unsigned int irq, unsigned long handle)
+{
+ struct intc_desc_int *d = get_intc_desc(irq);
+ unsigned long addr;
+ unsigned int cpu;
+
+ for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
+#ifdef CONFIG_SMP
+ if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
+ continue;
+#endif
+ addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
+ intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
+ [_INTC_FN(handle)], irq);
+ }
+
+ intc_balancing_enable(irq);
+}
+
+static void intc_enable(unsigned int irq)
+{
+ _intc_enable(irq, (unsigned long)get_irq_chip_data(irq));
+}
+
+static void intc_disable(unsigned int irq)
+{
+ struct intc_desc_int *d = get_intc_desc(irq);
+ unsigned long handle = (unsigned long)get_irq_chip_data(irq);
+ unsigned long addr;
+ unsigned int cpu;
+
+ intc_balancing_disable(irq);
+
+ for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
+#ifdef CONFIG_SMP
+ if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
+ continue;
+#endif
+ addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
+ intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
+ [_INTC_FN(handle)], irq);
+ }
+}
+
+static int intc_set_wake(unsigned int irq, unsigned int on)
+{
+ return 0; /* allow wakeup, but setup hardware in intc_suspend() */
+}
+
+#ifdef CONFIG_SMP
+/*
+ * This is held with the irq desc lock held, so we don't require any
+ * additional locking here at the intc desc level. The affinity mask is
+ * later tested in the enable/disable paths.
+ */
+static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask)
+{
+ if (!cpumask_intersects(cpumask, cpu_online_mask))
+ return -1;
+
+ cpumask_copy(irq_to_desc(irq)->affinity, cpumask);
+
+ return 0;
+}
+#endif
+
+static void intc_mask_ack(unsigned int irq)
+{
+ struct intc_desc_int *d = get_intc_desc(irq);
+ unsigned long handle = intc_get_ack_handle(irq);
+ unsigned long addr;
+
+ intc_disable(irq);
+
+ /* read register and write zero only to the associated bit */
+ if (handle) {
+ unsigned int value;
+
+ addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
+ value = intc_set_field_from_handle(0, 1, handle);
+
+ switch (_INTC_FN(handle)) {
+ case REG_FN_MODIFY_BASE + 0: /* 8bit */
+ __raw_readb(addr);
+ __raw_writeb(0xff ^ value, addr);
+ break;
+ case REG_FN_MODIFY_BASE + 1: /* 16bit */
+ __raw_readw(addr);
+ __raw_writew(0xffff ^ value, addr);
+ break;
+ case REG_FN_MODIFY_BASE + 3: /* 32bit */
+ __raw_readl(addr);
+ __raw_writel(0xffffffff ^ value, addr);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ }
+}
+
+static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
+ unsigned int nr_hp,
+ unsigned int irq)
+{
+ int i;
+
+ /*
+ * this doesn't scale well, but...
+ *
+ * this function should only be used for cerain uncommon
+ * operations such as intc_set_priority() and intc_set_type()
+ * and in those rare cases performance doesn't matter that much.
+ * keeping the memory footprint low is more important.
+ *
+ * one rather simple way to speed this up and still keep the
+ * memory footprint down is to make sure the array is sorted
+ * and then perform a bisect to lookup the irq.
+ */
+ for (i = 0; i < nr_hp; i++) {
+ if ((hp + i)->irq != irq)
+ continue;
+
+ return hp + i;
+ }
+
+ return NULL;
+}
+
+int intc_set_priority(unsigned int irq, unsigned int prio)
+{
+ struct intc_desc_int *d = get_intc_desc(irq);
+ struct intc_handle_int *ihp;
+
+ if (!intc_get_prio_level(irq) || prio <= 1)
+ return -EINVAL;
+
+ ihp = intc_find_irq(d->prio, d->nr_prio, irq);
+ if (ihp) {
+ if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
+ return -EINVAL;
+
+ intc_set_prio_level(irq, prio);
+
+ /*
+ * only set secondary masking method directly
+ * primary masking method is using intc_prio_level[irq]
+ * priority level will be set during next enable()
+ */
+ if (_INTC_FN(ihp->handle) != REG_FN_ERR)
+ _intc_enable(irq, ihp->handle);
+ }
+ return 0;
+}
+
+#define VALID(x) (x | 0x80)
+
+static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
+ [IRQ_TYPE_EDGE_FALLING] = VALID(0),
+ [IRQ_TYPE_EDGE_RISING] = VALID(1),
+ [IRQ_TYPE_LEVEL_LOW] = VALID(2),
+ /* SH7706, SH7707 and SH7709 do not support high level triggered */
+#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
+ !defined(CONFIG_CPU_SUBTYPE_SH7707) && \
+ !defined(CONFIG_CPU_SUBTYPE_SH7709)
+ [IRQ_TYPE_LEVEL_HIGH] = VALID(3),
+#endif
+};
+
+static int intc_set_type(unsigned int irq, unsigned int type)
+{
+ struct intc_desc_int *d = get_intc_desc(irq);
+ unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
+ struct intc_handle_int *ihp;
+ unsigned long addr;
+
+ if (!value)
+ return -EINVAL;
+
+ ihp = intc_find_irq(d->sense, d->nr_sense, irq);
+ if (ihp) {
+ addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
+ intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
+ }
+
+ return 0;
+}
+
+struct irq_chip intc_irq_chip = {
+ .mask = intc_disable,
+ .unmask = intc_enable,
+ .mask_ack = intc_mask_ack,
+ .enable = intc_enable,
+ .disable = intc_disable,
+ .shutdown = intc_disable,
+ .set_type = intc_set_type,
+ .set_wake = intc_set_wake,
+#ifdef CONFIG_SMP
+ .set_affinity = intc_set_affinity,
+#endif
+};
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
new file mode 100644
index 000000000000..306ed287077a
--- /dev/null
+++ b/drivers/sh/intc/core.c
@@ -0,0 +1,469 @@
+/*
+ * Shared interrupt handling code for IPR and INTC2 types of IRQs.
+ *
+ * Copyright (C) 2007, 2008 Magnus Damm
+ * Copyright (C) 2009, 2010 Paul Mundt
+ *
+ * Based on intc2.c and ipr.c
+ *
+ * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi
+ * Copyright (C) 2000 Kazumoto Kojima
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ * Copyright (C) 2005, 2006 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#define pr_fmt(fmt) "intc: " fmt
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/sh_intc.h>
+#include <linux/sysdev.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/radix-tree.h>
+#include "internals.h"
+
+LIST_HEAD(intc_list);
+DEFINE_RAW_SPINLOCK(intc_big_lock);
+unsigned int nr_intc_controllers;
+
+/*
+ * Default priority level
+ * - this needs to be at least 2 for 5-bit priorities on 7780
+ */
+static unsigned int default_prio_level = 2; /* 2 - 16 */
+static unsigned int intc_prio_level[NR_IRQS]; /* for now */
+
+unsigned int intc_get_dfl_prio_level(void)
+{
+ return default_prio_level;
+}
+
+unsigned int intc_get_prio_level(unsigned int irq)
+{
+ return intc_prio_level[irq];
+}
+
+void intc_set_prio_level(unsigned int irq, unsigned int level)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&intc_big_lock, flags);
+ intc_prio_level[irq] = level;
+ raw_spin_unlock_irqrestore(&intc_big_lock, flags);
+}
+
+static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
+{
+ generic_handle_irq((unsigned int)get_irq_data(irq));
+}
+
+static void __init intc_register_irq(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id,
+ unsigned int irq)
+{
+ struct intc_handle_int *hp;
+ unsigned int data[2], primary;
+ unsigned long flags;
+
+ /*
+ * Register the IRQ position with the global IRQ map, then insert
+ * it in to the radix tree.
+ */
+ reserve_irq_vector(irq);
+
+ raw_spin_lock_irqsave(&intc_big_lock, flags);
+ radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq));
+ raw_spin_unlock_irqrestore(&intc_big_lock, flags);
+
+ /*
+ * Prefer single interrupt source bitmap over other combinations:
+ *
+ * 1. bitmap, single interrupt source
+ * 2. priority, single interrupt source
+ * 3. bitmap, multiple interrupt sources (groups)
+ * 4. priority, multiple interrupt sources (groups)
+ */
+ data[0] = intc_get_mask_handle(desc, d, enum_id, 0);
+ data[1] = intc_get_prio_handle(desc, d, enum_id, 0);
+
+ primary = 0;
+ if (!data[0] && data[1])
+ primary = 1;
+
+ if (!data[0] && !data[1])
+ pr_warning("missing unique irq mask for irq %d (vect 0x%04x)\n",
+ irq, irq2evt(irq));
+
+ data[0] = data[0] ? data[0] : intc_get_mask_handle(desc, d, enum_id, 1);
+ data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, 1);
+
+ if (!data[primary])
+ primary ^= 1;
+
+ BUG_ON(!data[primary]); /* must have primary masking method */
+
+ disable_irq_nosync(irq);
+ set_irq_chip_and_handler_name(irq, &d->chip,
+ handle_level_irq, "level");
+ set_irq_chip_data(irq, (void *)data[primary]);
+
+ /*
+ * set priority level
+ */
+ intc_set_prio_level(irq, intc_get_dfl_prio_level());
+
+ /* enable secondary masking method if present */
+ if (data[!primary])
+ _intc_enable(irq, data[!primary]);
+
+ /* add irq to d->prio list if priority is available */
+ if (data[1]) {
+ hp = d->prio + d->nr_prio;
+ hp->irq = irq;
+ hp->handle = data[1];
+
+ if (primary) {
+ /*
+ * only secondary priority should access registers, so
+ * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority()
+ */
+ hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0);
+ hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0);
+ }
+ d->nr_prio++;
+ }
+
+ /* add irq to d->sense list if sense is available */
+ data[0] = intc_get_sense_handle(desc, d, enum_id);
+ if (data[0]) {
+ (d->sense + d->nr_sense)->irq = irq;
+ (d->sense + d->nr_sense)->handle = data[0];
+ d->nr_sense++;
+ }
+
+ /* irq should be disabled by default */
+ d->chip.mask(irq);
+
+ intc_set_ack_handle(irq, desc, d, enum_id);
+ intc_set_dist_handle(irq, desc, d, enum_id);
+
+ activate_irq(irq);
+}
+
+static unsigned int __init save_reg(struct intc_desc_int *d,
+ unsigned int cnt,
+ unsigned long value,
+ unsigned int smp)
+{
+ if (value) {
+ value = intc_phys_to_virt(d, value);
+
+ d->reg[cnt] = value;
+#ifdef CONFIG_SMP
+ d->smp[cnt] = smp;
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+int __init register_intc_controller(struct intc_desc *desc)
+{
+ unsigned int i, k, smp;
+ struct intc_hw_desc *hw = &desc->hw;
+ struct intc_desc_int *d;
+ struct resource *res;
+
+ pr_info("Registered controller '%s' with %u IRQs\n",
+ desc->name, hw->nr_vectors);
+
+ d = kzalloc(sizeof(*d), GFP_NOWAIT);
+ if (!d)
+ goto err0;
+
+ INIT_LIST_HEAD(&d->list);
+ list_add_tail(&d->list, &intc_list);
+
+ raw_spin_lock_init(&d->lock);
+
+ d->index = nr_intc_controllers;
+
+ if (desc->num_resources) {
+ d->nr_windows = desc->num_resources;
+ d->window = kzalloc(d->nr_windows * sizeof(*d->window),
+ GFP_NOWAIT);
+ if (!d->window)
+ goto err1;
+
+ for (k = 0; k < d->nr_windows; k++) {
+ res = desc->resource + k;
+ WARN_ON(resource_type(res) != IORESOURCE_MEM);
+ d->window[k].phys = res->start;
+ d->window[k].size = resource_size(res);
+ d->window[k].virt = ioremap_nocache(res->start,
+ resource_size(res));
+ if (!d->window[k].virt)
+ goto err2;
+ }
+ }
+
+ d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
+#ifdef CONFIG_INTC_BALANCING
+ if (d->nr_reg)
+ d->nr_reg += hw->nr_mask_regs;
+#endif
+ d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
+ d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
+ d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
+ d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0;
+
+ d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
+ if (!d->reg)
+ goto err2;
+
+#ifdef CONFIG_SMP
+ d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
+ if (!d->smp)
+ goto err3;
+#endif
+ k = 0;
+
+ if (hw->mask_regs) {
+ for (i = 0; i < hw->nr_mask_regs; i++) {
+ smp = IS_SMP(hw->mask_regs[i]);
+ k += save_reg(d, k, hw->mask_regs[i].set_reg, smp);
+ k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp);
+#ifdef CONFIG_INTC_BALANCING
+ k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0);
+#endif
+ }
+ }
+
+ if (hw->prio_regs) {
+ d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio),
+ GFP_NOWAIT);
+ if (!d->prio)
+ goto err4;
+
+ for (i = 0; i < hw->nr_prio_regs; i++) {
+ smp = IS_SMP(hw->prio_regs[i]);
+ k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
+ k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
+ }
+ }
+
+ if (hw->sense_regs) {
+ d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense),
+ GFP_NOWAIT);
+ if (!d->sense)
+ goto err5;
+
+ for (i = 0; i < hw->nr_sense_regs; i++)
+ k += save_reg(d, k, hw->sense_regs[i].reg, 0);
+ }
+
+ if (hw->subgroups)
+ for (i = 0; i < hw->nr_subgroups; i++)
+ if (hw->subgroups[i].reg)
+ k+= save_reg(d, k, hw->subgroups[i].reg, 0);
+
+ memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip));
+ d->chip.name = desc->name;
+
+ if (hw->ack_regs)
+ for (i = 0; i < hw->nr_ack_regs; i++)
+ k += save_reg(d, k, hw->ack_regs[i].set_reg, 0);
+ else
+ d->chip.mask_ack = d->chip.disable;
+
+ /* disable bits matching force_disable before registering irqs */
+ if (desc->force_disable)
+ intc_enable_disable_enum(desc, d, desc->force_disable, 0);
+
+ /* disable bits matching force_enable before registering irqs */
+ if (desc->force_enable)
+ intc_enable_disable_enum(desc, d, desc->force_enable, 0);
+
+ BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
+
+ /* register the vectors one by one */
+ for (i = 0; i < hw->nr_vectors; i++) {
+ struct intc_vect *vect = hw->vectors + i;
+ unsigned int irq = evt2irq(vect->vect);
+ struct irq_desc *irq_desc;
+
+ if (!vect->enum_id)
+ continue;
+
+ irq_desc = irq_to_desc_alloc_node(irq, numa_node_id());
+ if (unlikely(!irq_desc)) {
+ pr_err("can't get irq_desc for %d\n", irq);
+ continue;
+ }
+
+ intc_irq_xlate_set(irq, vect->enum_id, d);
+ intc_register_irq(desc, d, vect->enum_id, irq);
+
+ for (k = i + 1; k < hw->nr_vectors; k++) {
+ struct intc_vect *vect2 = hw->vectors + k;
+ unsigned int irq2 = evt2irq(vect2->vect);
+
+ if (vect->enum_id != vect2->enum_id)
+ continue;
+
+ /*
+ * In the case of multi-evt handling and sparse
+ * IRQ support, each vector still needs to have
+ * its own backing irq_desc.
+ */
+ irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id());
+ if (unlikely(!irq_desc)) {
+ pr_err("can't get irq_desc for %d\n", irq2);
+ continue;
+ }
+
+ vect2->enum_id = 0;
+
+ /* redirect this interrupts to the first one */
+ set_irq_chip(irq2, &dummy_irq_chip);
+ set_irq_chained_handler(irq2, intc_redirect_irq);
+ set_irq_data(irq2, (void *)irq);
+ }
+ }
+
+ intc_subgroup_init(desc, d);
+
+ /* enable bits matching force_enable after registering irqs */
+ if (desc->force_enable)
+ intc_enable_disable_enum(desc, d, desc->force_enable, 1);
+
+ nr_intc_controllers++;
+
+ return 0;
+err5:
+ kfree(d->prio);
+err4:
+#ifdef CONFIG_SMP
+ kfree(d->smp);
+err3:
+#endif
+ kfree(d->reg);
+err2:
+ for (k = 0; k < d->nr_windows; k++)
+ if (d->window[k].virt)
+ iounmap(d->window[k].virt);
+
+ kfree(d->window);
+err1:
+ kfree(d);
+err0:
+ pr_err("unable to allocate INTC memory\n");
+
+ return -ENOMEM;
+}
+
+static ssize_t
+show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
+{
+ struct intc_desc_int *d;
+
+ d = container_of(dev, struct intc_desc_int, sysdev);
+
+ return sprintf(buf, "%s\n", d->chip.name);
+}
+
+static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL);
+
+static int intc_suspend(struct sys_device *dev, pm_message_t state)
+{
+ struct intc_desc_int *d;
+ struct irq_desc *desc;
+ int irq;
+
+ /* get intc controller associated with this sysdev */
+ d = container_of(dev, struct intc_desc_int, sysdev);
+
+ switch (state.event) {
+ case PM_EVENT_ON:
+ if (d->state.event != PM_EVENT_FREEZE)
+ break;
+
+ for_each_irq_desc(irq, desc) {
+ /*
+ * This will catch the redirect and VIRQ cases
+ * due to the dummy_irq_chip being inserted.
+ */
+ if (desc->chip != &d->chip)
+ continue;
+ if (desc->status & IRQ_DISABLED)
+ desc->chip->disable(irq);
+ else
+ desc->chip->enable(irq);
+ }
+ break;
+ case PM_EVENT_FREEZE:
+ /* nothing has to be done */
+ break;
+ case PM_EVENT_SUSPEND:
+ /* enable wakeup irqs belonging to this intc controller */
+ for_each_irq_desc(irq, desc) {
+ if (desc->chip != &d->chip)
+ continue;
+ if ((desc->status & IRQ_WAKEUP))
+ desc->chip->enable(irq);
+ }
+ break;
+ }
+
+ d->state = state;
+
+ return 0;
+}
+
+static int intc_resume(struct sys_device *dev)
+{
+ return intc_suspend(dev, PMSG_ON);
+}
+
+struct sysdev_class intc_sysdev_class = {
+ .name = "intc",
+ .suspend = intc_suspend,
+ .resume = intc_resume,
+};
+
+/* register this intc as sysdev to allow suspend/resume */
+static int __init register_intc_sysdevs(void)
+{
+ struct intc_desc_int *d;
+ int error;
+
+ error = sysdev_class_register(&intc_sysdev_class);
+ if (!error) {
+ list_for_each_entry(d, &intc_list, list) {
+ d->sysdev.id = d->index;
+ d->sysdev.cls = &intc_sysdev_class;
+ error = sysdev_register(&d->sysdev);
+ if (error == 0)
+ error = sysdev_create_file(&d->sysdev,
+ &attr_name);
+ if (error)
+ break;
+ }
+ }
+
+ if (error)
+ pr_err("sysdev registration error\n");
+
+ return error;
+}
+device_initcall(register_intc_sysdevs);
diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c
new file mode 100644
index 000000000000..6caecdffe201
--- /dev/null
+++ b/drivers/sh/intc/dynamic.c
@@ -0,0 +1,135 @@
+/*
+ * Dynamic IRQ management
+ *
+ * Copyright (C) 2010 Paul Mundt
+ *
+ * Modelled after arch/x86/kernel/apic/io_apic.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#define pr_fmt(fmt) "intc: " fmt
+
+#include <linux/irq.h>
+#include <linux/bitmap.h>
+#include <linux/spinlock.h>
+#include "internals.h" /* only for activate_irq() damage.. */
+
+/*
+ * The intc_irq_map provides a global map of bound IRQ vectors for a
+ * given platform. Allocation of IRQs are either static through the CPU
+ * vector map, or dynamic in the case of board mux vectors or MSI.
+ *
+ * As this is a central point for all IRQ controllers on the system,
+ * each of the available sources are mapped out here. This combined with
+ * sparseirq makes it quite trivial to keep the vector map tightly packed
+ * when dynamically creating IRQs, as well as tying in to otherwise
+ * unused irq_desc positions in the sparse array.
+ */
+static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
+static DEFINE_RAW_SPINLOCK(vector_lock);
+
+/*
+ * Dynamic IRQ allocation and deallocation
+ */
+unsigned int create_irq_nr(unsigned int irq_want, int node)
+{
+ unsigned int irq = 0, new;
+ unsigned long flags;
+ struct irq_desc *desc;
+
+ raw_spin_lock_irqsave(&vector_lock, flags);
+
+ /*
+ * First try the wanted IRQ
+ */
+ if (test_and_set_bit(irq_want, intc_irq_map) == 0) {
+ new = irq_want;
+ } else {
+ /* .. then fall back to scanning. */
+ new = find_first_zero_bit(intc_irq_map, nr_irqs);
+ if (unlikely(new == nr_irqs))
+ goto out_unlock;
+
+ __set_bit(new, intc_irq_map);
+ }
+
+ desc = irq_to_desc_alloc_node(new, node);
+ if (unlikely(!desc)) {
+ pr_err("can't get irq_desc for %d\n", new);
+ goto out_unlock;
+ }
+
+ desc = move_irq_desc(desc, node);
+ irq = new;
+
+out_unlock:
+ raw_spin_unlock_irqrestore(&vector_lock, flags);
+
+ if (irq > 0) {
+ dynamic_irq_init(irq);
+ activate_irq(irq);
+ }
+
+ return irq;
+}
+
+int create_irq(void)
+{
+ int nid = cpu_to_node(smp_processor_id());
+ int irq;
+
+ irq = create_irq_nr(NR_IRQS_LEGACY, nid);
+ if (irq == 0)
+ irq = -1;
+
+ return irq;
+}
+
+void destroy_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ dynamic_irq_cleanup(irq);
+
+ raw_spin_lock_irqsave(&vector_lock, flags);
+ __clear_bit(irq, intc_irq_map);
+ raw_spin_unlock_irqrestore(&vector_lock, flags);
+}
+
+int reserve_irq_vector(unsigned int irq)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ raw_spin_lock_irqsave(&vector_lock, flags);
+ if (test_and_set_bit(irq, intc_irq_map))
+ ret = -EBUSY;
+ raw_spin_unlock_irqrestore(&vector_lock, flags);
+
+ return ret;
+}
+
+void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs)
+{
+ unsigned long flags;
+ int i;
+
+ raw_spin_lock_irqsave(&vector_lock, flags);
+ for (i = 0; i < nr_vecs; i++)
+ __set_bit(evt2irq(vectors[i].vect), intc_irq_map);
+ raw_spin_unlock_irqrestore(&vector_lock, flags);
+}
+
+void reserve_irq_legacy(void)
+{
+ unsigned long flags;
+ int i, j;
+
+ raw_spin_lock_irqsave(&vector_lock, flags);
+ j = find_first_bit(intc_irq_map, nr_irqs);
+ for (i = 0; i < j; i++)
+ __set_bit(i, intc_irq_map);
+ raw_spin_unlock_irqrestore(&vector_lock, flags);
+}
diff --git a/drivers/sh/intc/handle.c b/drivers/sh/intc/handle.c
new file mode 100644
index 000000000000..057ce56829bf
--- /dev/null
+++ b/drivers/sh/intc/handle.c
@@ -0,0 +1,307 @@
+/*
+ * Shared interrupt handling code for IPR and INTC2 types of IRQs.
+ *
+ * Copyright (C) 2007, 2008 Magnus Damm
+ * Copyright (C) 2009, 2010 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include "internals.h"
+
+static unsigned long ack_handle[NR_IRQS];
+
+static intc_enum __init intc_grp_id(struct intc_desc *desc,
+ intc_enum enum_id)
+{
+ struct intc_group *g = desc->hw.groups;
+ unsigned int i, j;
+
+ for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) {
+ g = desc->hw.groups + i;
+
+ for (j = 0; g->enum_ids[j]; j++) {
+ if (g->enum_ids[j] != enum_id)
+ continue;
+
+ return g->enum_id;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int __init _intc_mask_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id,
+ unsigned int *reg_idx,
+ unsigned int *fld_idx)
+{
+ struct intc_mask_reg *mr = desc->hw.mask_regs;
+ unsigned int fn, mode;
+ unsigned long reg_e, reg_d;
+
+ while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) {
+ mr = desc->hw.mask_regs + *reg_idx;
+
+ for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) {
+ if (mr->enum_ids[*fld_idx] != enum_id)
+ continue;
+
+ if (mr->set_reg && mr->clr_reg) {
+ fn = REG_FN_WRITE_BASE;
+ mode = MODE_DUAL_REG;
+ reg_e = mr->clr_reg;
+ reg_d = mr->set_reg;
+ } else {
+ fn = REG_FN_MODIFY_BASE;
+ if (mr->set_reg) {
+ mode = MODE_ENABLE_REG;
+ reg_e = mr->set_reg;
+ reg_d = mr->set_reg;
+ } else {
+ mode = MODE_MASK_REG;
+ reg_e = mr->clr_reg;
+ reg_d = mr->clr_reg;
+ }
+ }
+
+ fn += (mr->reg_width >> 3) - 1;
+ return _INTC_MK(fn, mode,
+ intc_get_reg(d, reg_e),
+ intc_get_reg(d, reg_d),
+ 1,
+ (mr->reg_width - 1) - *fld_idx);
+ }
+
+ *fld_idx = 0;
+ (*reg_idx)++;
+ }
+
+ return 0;
+}
+
+unsigned int __init
+intc_get_mask_handle(struct intc_desc *desc, struct intc_desc_int *d,
+ intc_enum enum_id, int do_grps)
+{
+ unsigned int i = 0;
+ unsigned int j = 0;
+ unsigned int ret;
+
+ ret = _intc_mask_data(desc, d, enum_id, &i, &j);
+ if (ret)
+ return ret;
+
+ if (do_grps)
+ return intc_get_mask_handle(desc, d, intc_grp_id(desc, enum_id), 0);
+
+ return 0;
+}
+
+static unsigned int __init _intc_prio_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id,
+ unsigned int *reg_idx,
+ unsigned int *fld_idx)
+{
+ struct intc_prio_reg *pr = desc->hw.prio_regs;
+ unsigned int fn, n, mode, bit;
+ unsigned long reg_e, reg_d;
+
+ while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) {
+ pr = desc->hw.prio_regs + *reg_idx;
+
+ for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) {
+ if (pr->enum_ids[*fld_idx] != enum_id)
+ continue;
+
+ if (pr->set_reg && pr->clr_reg) {
+ fn = REG_FN_WRITE_BASE;
+ mode = MODE_PCLR_REG;
+ reg_e = pr->set_reg;
+ reg_d = pr->clr_reg;
+ } else {
+ fn = REG_FN_MODIFY_BASE;
+ mode = MODE_PRIO_REG;
+ if (!pr->set_reg)
+ BUG();
+ reg_e = pr->set_reg;
+ reg_d = pr->set_reg;
+ }
+
+ fn += (pr->reg_width >> 3) - 1;
+ n = *fld_idx + 1;
+
+ BUG_ON(n * pr->field_width > pr->reg_width);
+
+ bit = pr->reg_width - (n * pr->field_width);
+
+ return _INTC_MK(fn, mode,
+ intc_get_reg(d, reg_e),
+ intc_get_reg(d, reg_d),
+ pr->field_width, bit);
+ }
+
+ *fld_idx = 0;
+ (*reg_idx)++;
+ }
+
+ return 0;
+}
+
+unsigned int __init
+intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,
+ intc_enum enum_id, int do_grps)
+{
+ unsigned int i = 0;
+ unsigned int j = 0;
+ unsigned int ret;
+
+ ret = _intc_prio_data(desc, d, enum_id, &i, &j);
+ if (ret)
+ return ret;
+
+ if (do_grps)
+ return intc_get_prio_handle(desc, d, intc_grp_id(desc, enum_id), 0);
+
+ return 0;
+}
+
+static unsigned int __init intc_ack_data(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id)
+{
+ struct intc_mask_reg *mr = desc->hw.ack_regs;
+ unsigned int i, j, fn, mode;
+ unsigned long reg_e, reg_d;
+
+ for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) {
+ mr = desc->hw.ack_regs + i;
+
+ for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
+ if (mr->enum_ids[j] != enum_id)
+ continue;
+
+ fn = REG_FN_MODIFY_BASE;
+ mode = MODE_ENABLE_REG;
+ reg_e = mr->set_reg;
+ reg_d = mr->set_reg;
+
+ fn += (mr->reg_width >> 3) - 1;
+ return _INTC_MK(fn, mode,
+ intc_get_reg(d, reg_e),
+ intc_get_reg(d, reg_d),
+ 1,
+ (mr->reg_width - 1) - j);
+ }
+ }
+
+ return 0;
+}
+
+static void intc_enable_disable(struct intc_desc_int *d,
+ unsigned long handle, int do_enable)
+{
+ unsigned long addr;
+ unsigned int cpu;
+ unsigned long (*fn)(unsigned long, unsigned long,
+ unsigned long (*)(unsigned long, unsigned long,
+ unsigned long),
+ unsigned int);
+
+ if (do_enable) {
+ for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
+ addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
+ fn = intc_enable_noprio_fns[_INTC_MODE(handle)];
+ fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
+ }
+ } else {
+ for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
+ addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
+ fn = intc_disable_fns[_INTC_MODE(handle)];
+ fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
+ }
+ }
+}
+
+void __init intc_enable_disable_enum(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id, int enable)
+{
+ unsigned int i, j, data;
+
+ /* go through and enable/disable all mask bits */
+ i = j = 0;
+ do {
+ data = _intc_mask_data(desc, d, enum_id, &i, &j);
+ if (data)
+ intc_enable_disable(d, data, enable);
+ j++;
+ } while (data);
+
+ /* go through and enable/disable all priority fields */
+ i = j = 0;
+ do {
+ data = _intc_prio_data(desc, d, enum_id, &i, &j);
+ if (data)
+ intc_enable_disable(d, data, enable);
+
+ j++;
+ } while (data);
+}
+
+unsigned int __init
+intc_get_sense_handle(struct intc_desc *desc, struct intc_desc_int *d,
+ intc_enum enum_id)
+{
+ struct intc_sense_reg *sr = desc->hw.sense_regs;
+ unsigned int i, j, fn, bit;
+
+ for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) {
+ sr = desc->hw.sense_regs + i;
+
+ for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) {
+ if (sr->enum_ids[j] != enum_id)
+ continue;
+
+ fn = REG_FN_MODIFY_BASE;
+ fn += (sr->reg_width >> 3) - 1;
+
+ BUG_ON((j + 1) * sr->field_width > sr->reg_width);
+
+ bit = sr->reg_width - ((j + 1) * sr->field_width);
+
+ return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg),
+ 0, sr->field_width, bit);
+ }
+ }
+
+ return 0;
+}
+
+
+void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc,
+ struct intc_desc_int *d, intc_enum id)
+{
+ unsigned long flags;
+
+ /*
+ * Nothing to do for this IRQ.
+ */
+ if (!desc->hw.ack_regs)
+ return;
+
+ raw_spin_lock_irqsave(&intc_big_lock, flags);
+ ack_handle[irq] = intc_ack_data(desc, d, id);
+ raw_spin_unlock_irqrestore(&intc_big_lock, flags);
+}
+
+unsigned long intc_get_ack_handle(unsigned int irq)
+{
+ return ack_handle[irq];
+}
diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h
new file mode 100644
index 000000000000..d49482c623fa
--- /dev/null
+++ b/drivers/sh/intc/internals.h
@@ -0,0 +1,186 @@
+#include <linux/sh_intc.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/radix-tree.h>
+#include <linux/sysdev.h>
+
+#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
+ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
+ ((addr_e) << 16) | ((addr_d << 24)))
+
+#define _INTC_SHIFT(h) (h & 0x1f)
+#define _INTC_WIDTH(h) ((h >> 5) & 0xf)
+#define _INTC_FN(h) ((h >> 9) & 0xf)
+#define _INTC_MODE(h) ((h >> 13) & 0x7)
+#define _INTC_ADDR_E(h) ((h >> 16) & 0xff)
+#define _INTC_ADDR_D(h) ((h >> 24) & 0xff)
+
+#ifdef CONFIG_SMP
+#define IS_SMP(x) (x.smp)
+#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c))
+#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1)
+#else
+#define IS_SMP(x) 0
+#define INTC_REG(d, x, c) (d->reg[(x)])
+#define SMP_NR(d, x) 1
+#endif
+
+struct intc_handle_int {
+ unsigned int irq;
+ unsigned long handle;
+};
+
+struct intc_window {
+ phys_addr_t phys;
+ void __iomem *virt;
+ unsigned long size;
+};
+
+struct intc_map_entry {
+ intc_enum enum_id;
+ struct intc_desc_int *desc;
+};
+
+struct intc_subgroup_entry {
+ unsigned int pirq;
+ intc_enum enum_id;
+ unsigned long handle;
+};
+
+struct intc_desc_int {
+ struct list_head list;
+ struct sys_device sysdev;
+ struct radix_tree_root tree;
+ pm_message_t state;
+ raw_spinlock_t lock;
+ unsigned int index;
+ unsigned long *reg;
+#ifdef CONFIG_SMP
+ unsigned long *smp;
+#endif
+ unsigned int nr_reg;
+ struct intc_handle_int *prio;
+ unsigned int nr_prio;
+ struct intc_handle_int *sense;
+ unsigned int nr_sense;
+ struct intc_window *window;
+ unsigned int nr_windows;
+ struct irq_chip chip;
+};
+
+
+enum {
+ REG_FN_ERR = 0,
+ REG_FN_TEST_BASE = 1,
+ REG_FN_WRITE_BASE = 5,
+ REG_FN_MODIFY_BASE = 9
+};
+
+enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */
+ MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */
+ MODE_DUAL_REG, /* Two registers, set bit to enable / disable */
+ MODE_PRIO_REG, /* Priority value written to enable interrupt */
+ MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */
+};
+
+static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
+{
+ struct irq_chip *chip = get_irq_chip(irq);
+
+ return container_of(chip, struct intc_desc_int, chip);
+}
+
+/*
+ * Grumble.
+ */
+static inline void activate_irq(int irq)
+{
+#ifdef CONFIG_ARM
+ /* ARM requires an extra step to clear IRQ_NOREQUEST, which it
+ * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE.
+ */
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ /* same effect on other architectures */
+ set_irq_noprobe(irq);
+#endif
+}
+
+/* access.c */
+extern unsigned long
+(*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data);
+
+extern unsigned long
+(*intc_enable_fns[])(unsigned long addr, unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long, unsigned long),
+ unsigned int irq);
+extern unsigned long
+(*intc_disable_fns[])(unsigned long addr, unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long, unsigned long),
+ unsigned int irq);
+extern unsigned long
+(*intc_enable_noprio_fns[])(unsigned long addr, unsigned long handle,
+ unsigned long (*fn)(unsigned long,
+ unsigned long, unsigned long),
+ unsigned int irq);
+
+unsigned long intc_phys_to_virt(struct intc_desc_int *d, unsigned long address);
+unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address);
+unsigned int intc_set_field_from_handle(unsigned int value,
+ unsigned int field_value,
+ unsigned int handle);
+unsigned long intc_get_field_from_handle(unsigned int value,
+ unsigned int handle);
+
+/* balancing.c */
+#ifdef CONFIG_INTC_BALANCING
+void intc_balancing_enable(unsigned int irq);
+void intc_balancing_disable(unsigned int irq);
+void intc_set_dist_handle(unsigned int irq, struct intc_desc *desc,
+ struct intc_desc_int *d, intc_enum id);
+#else
+static inline void intc_balancing_enable(unsigned int irq) { }
+static inline void intc_balancing_disable(unsigned int irq) { }
+static inline void
+intc_set_dist_handle(unsigned int irq, struct intc_desc *desc,
+ struct intc_desc_int *d, intc_enum id) { }
+#endif
+
+/* chip.c */
+extern struct irq_chip intc_irq_chip;
+void _intc_enable(unsigned int irq, unsigned long handle);
+
+/* core.c */
+extern struct list_head intc_list;
+extern raw_spinlock_t intc_big_lock;
+extern unsigned int nr_intc_controllers;
+extern struct sysdev_class intc_sysdev_class;
+
+unsigned int intc_get_dfl_prio_level(void);
+unsigned int intc_get_prio_level(unsigned int irq);
+void intc_set_prio_level(unsigned int irq, unsigned int level);
+
+/* handle.c */
+unsigned int intc_get_mask_handle(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id, int do_grps);
+unsigned int intc_get_prio_handle(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id, int do_grps);
+unsigned int intc_get_sense_handle(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ intc_enum enum_id);
+void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc,
+ struct intc_desc_int *d, intc_enum id);
+unsigned long intc_get_ack_handle(unsigned int irq);
+void intc_enable_disable_enum(struct intc_desc *desc, struct intc_desc_int *d,
+ intc_enum enum_id, int enable);
+
+/* virq.c */
+void intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d);
+void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d);
+struct intc_map_entry *intc_irq_xlate_get(unsigned int irq);
diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c
new file mode 100644
index 000000000000..e32304b66cf1
--- /dev/null
+++ b/drivers/sh/intc/userimask.c
@@ -0,0 +1,83 @@
+/*
+ * Support for hardware-assisted userspace interrupt masking.
+ *
+ * Copyright (C) 2010 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#define pr_fmt(fmt) "intc: " fmt
+
+#include <linux/errno.h>
+#include <linux/sysdev.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <asm/sizes.h>
+#include "internals.h"
+
+static void __iomem *uimask;
+
+static ssize_t
+show_intc_userimask(struct sysdev_class *cls,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);
+}
+
+static ssize_t
+store_intc_userimask(struct sysdev_class *cls,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long level;
+
+ level = simple_strtoul(buf, NULL, 10);
+
+ /*
+ * Minimal acceptable IRQ levels are in the 2 - 16 range, but
+ * these are chomped so as to not interfere with normal IRQs.
+ *
+ * Level 1 is a special case on some CPUs in that it's not
+ * directly settable, but given that USERIMASK cuts off below a
+ * certain level, we don't care about this limitation here.
+ * Level 0 on the other hand equates to user masking disabled.
+ *
+ * We use the default priority level as a cut off so that only
+ * special case opt-in IRQs can be mangled.
+ */
+ if (level >= intc_get_dfl_prio_level())
+ return -EINVAL;
+
+ __raw_writel(0xa5 << 24 | level << 4, uimask);
+
+ return count;
+}
+
+static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR,
+ show_intc_userimask, store_intc_userimask);
+
+
+static int __init userimask_sysdev_init(void)
+{
+ if (unlikely(!uimask))
+ return -ENXIO;
+
+ return sysdev_class_create_file(&intc_sysdev_class, &attr_userimask);
+}
+late_initcall(userimask_sysdev_init);
+
+int register_intc_userimask(unsigned long addr)
+{
+ if (unlikely(uimask))
+ return -EBUSY;
+
+ uimask = ioremap_nocache(addr, SZ_4K);
+ if (unlikely(!uimask))
+ return -ENOMEM;
+
+ pr_info("userimask support registered for levels 0 -> %d\n",
+ intc_get_dfl_prio_level() - 1);
+
+ return 0;
+}
diff --git a/drivers/sh/intc/virq-debugfs.c b/drivers/sh/intc/virq-debugfs.c
new file mode 100644
index 000000000000..9e62ba9311f0
--- /dev/null
+++ b/drivers/sh/intc/virq-debugfs.c
@@ -0,0 +1,64 @@
+/*
+ * Support for virtual IRQ subgroups debugfs mapping.
+ *
+ * Copyright (C) 2010 Paul Mundt
+ *
+ * Modelled after arch/powerpc/kernel/irq.c.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/seq_file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/debugfs.h>
+#include "internals.h"
+
+static int intc_irq_xlate_debug(struct seq_file *m, void *priv)
+{
+ int i;
+
+ seq_printf(m, "%-5s %-7s %-15s\n", "irq", "enum", "chip name");
+
+ for (i = 1; i < nr_irqs; i++) {
+ struct intc_map_entry *entry = intc_irq_xlate_get(i);
+ struct intc_desc_int *desc = entry->desc;
+
+ if (!desc)
+ continue;
+
+ seq_printf(m, "%5d ", i);
+ seq_printf(m, "0x%05x ", entry->enum_id);
+ seq_printf(m, "%-15s\n", desc->chip.name);
+ }
+
+ return 0;
+}
+
+static int intc_irq_xlate_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, intc_irq_xlate_debug, inode->i_private);
+}
+
+static const struct file_operations intc_irq_xlate_fops = {
+ .open = intc_irq_xlate_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init intc_irq_xlate_init(void)
+{
+ /*
+ * XXX.. use arch_debugfs_dir here when all of the intc users are
+ * converted.
+ */
+ if (debugfs_create_file("intc_irq_xlate", S_IRUGO, NULL, NULL,
+ &intc_irq_xlate_fops) == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+fs_initcall(intc_irq_xlate_init);
diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c
new file mode 100644
index 000000000000..643dfd4d2057
--- /dev/null
+++ b/drivers/sh/intc/virq.c
@@ -0,0 +1,255 @@
+/*
+ * Support for virtual IRQ subgroups.
+ *
+ * Copyright (C) 2010 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#define pr_fmt(fmt) "intc: " fmt
+
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/radix-tree.h>
+#include <linux/spinlock.h>
+#include "internals.h"
+
+static struct intc_map_entry intc_irq_xlate[NR_IRQS];
+
+struct intc_virq_list {
+ unsigned int irq;
+ struct intc_virq_list *next;
+};
+
+#define for_each_virq(entry, head) \
+ for (entry = head; entry; entry = entry->next)
+
+/*
+ * Tags for the radix tree
+ */
+#define INTC_TAG_VIRQ_NEEDS_ALLOC 0
+
+void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&intc_big_lock, flags);
+ intc_irq_xlate[irq].enum_id = id;
+ intc_irq_xlate[irq].desc = d;
+ raw_spin_unlock_irqrestore(&intc_big_lock, flags);
+}
+
+struct intc_map_entry *intc_irq_xlate_get(unsigned int irq)
+{
+ return intc_irq_xlate + irq;
+}
+
+int intc_irq_lookup(const char *chipname, intc_enum enum_id)
+{
+ struct intc_map_entry *ptr;
+ struct intc_desc_int *d;
+ int irq = -1;
+
+ list_for_each_entry(d, &intc_list, list) {
+ int tagged;
+
+ if (strcmp(d->chip.name, chipname) != 0)
+ continue;
+
+ /*
+ * Catch early lookups for subgroup VIRQs that have not
+ * yet been allocated an IRQ. This already includes a
+ * fast-path out if the tree is untagged, so there is no
+ * need to explicitly test the root tree.
+ */
+ tagged = radix_tree_tag_get(&d->tree, enum_id,
+ INTC_TAG_VIRQ_NEEDS_ALLOC);
+ if (unlikely(tagged))
+ break;
+
+ ptr = radix_tree_lookup(&d->tree, enum_id);
+ if (ptr) {
+ irq = ptr - intc_irq_xlate;
+ break;
+ }
+ }
+
+ return irq;
+}
+EXPORT_SYMBOL_GPL(intc_irq_lookup);
+
+static int add_virq_to_pirq(unsigned int irq, unsigned int virq)
+{
+ struct intc_virq_list **last, *entry;
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ /* scan for duplicates */
+ last = (struct intc_virq_list **)&desc->handler_data;
+ for_each_virq(entry, desc->handler_data) {
+ if (entry->irq == virq)
+ return 0;
+ last = &entry->next;
+ }
+
+ entry = kzalloc(sizeof(struct intc_virq_list), GFP_ATOMIC);
+ if (!entry) {
+ pr_err("can't allocate VIRQ mapping for %d\n", virq);
+ return -ENOMEM;
+ }
+
+ entry->irq = virq;
+
+ *last = entry;
+
+ return 0;
+}
+
+static void intc_virq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct intc_virq_list *entry, *vlist = get_irq_data(irq);
+ struct intc_desc_int *d = get_intc_desc(irq);
+
+ desc->chip->mask_ack(irq);
+
+ for_each_virq(entry, vlist) {
+ unsigned long addr, handle;
+
+ handle = (unsigned long)get_irq_data(entry->irq);
+ addr = INTC_REG(d, _INTC_ADDR_E(handle), 0);
+
+ if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0))
+ generic_handle_irq(entry->irq);
+ }
+
+ desc->chip->unmask(irq);
+}
+
+static unsigned long __init intc_subgroup_data(struct intc_subgroup *subgroup,
+ struct intc_desc_int *d,
+ unsigned int index)
+{
+ unsigned int fn = REG_FN_TEST_BASE + (subgroup->reg_width >> 3) - 1;
+
+ return _INTC_MK(fn, MODE_ENABLE_REG, intc_get_reg(d, subgroup->reg),
+ 0, 1, (subgroup->reg_width - 1) - index);
+}
+
+static void __init intc_subgroup_init_one(struct intc_desc *desc,
+ struct intc_desc_int *d,
+ struct intc_subgroup *subgroup)
+{
+ struct intc_map_entry *mapped;
+ unsigned int pirq;
+ unsigned long flags;
+ int i;
+
+ mapped = radix_tree_lookup(&d->tree, subgroup->parent_id);
+ if (!mapped) {
+ WARN_ON(1);
+ return;
+ }
+
+ pirq = mapped - intc_irq_xlate;
+
+ raw_spin_lock_irqsave(&d->lock, flags);
+
+ for (i = 0; i < ARRAY_SIZE(subgroup->enum_ids); i++) {
+ struct intc_subgroup_entry *entry;
+ int err;
+
+ if (!subgroup->enum_ids[i])
+ continue;
+
+ entry = kmalloc(sizeof(*entry), GFP_NOWAIT);
+ if (!entry)
+ break;
+
+ entry->pirq = pirq;
+ entry->enum_id = subgroup->enum_ids[i];
+ entry->handle = intc_subgroup_data(subgroup, d, i);
+
+ err = radix_tree_insert(&d->tree, entry->enum_id, entry);
+ if (unlikely(err < 0))
+ break;
+
+ radix_tree_tag_set(&d->tree, entry->enum_id,
+ INTC_TAG_VIRQ_NEEDS_ALLOC);
+ }
+
+ raw_spin_unlock_irqrestore(&d->lock, flags);
+}
+
+void __init intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d)
+{
+ int i;
+
+ if (!desc->hw.subgroups)
+ return;
+
+ for (i = 0; i < desc->hw.nr_subgroups; i++)
+ intc_subgroup_init_one(desc, d, desc->hw.subgroups + i);
+}
+
+static void __init intc_subgroup_map(struct intc_desc_int *d)
+{
+ struct intc_subgroup_entry *entries[32];
+ unsigned long flags;
+ unsigned int nr_found;
+ int i;
+
+ raw_spin_lock_irqsave(&d->lock, flags);
+
+restart:
+ nr_found = radix_tree_gang_lookup_tag_slot(&d->tree,
+ (void ***)entries, 0, ARRAY_SIZE(entries),
+ INTC_TAG_VIRQ_NEEDS_ALLOC);
+
+ for (i = 0; i < nr_found; i++) {
+ struct intc_subgroup_entry *entry;
+ int irq;
+
+ entry = radix_tree_deref_slot((void **)entries[i]);
+ if (unlikely(!entry))
+ continue;
+ if (unlikely(entry == RADIX_TREE_RETRY))
+ goto restart;
+
+ irq = create_irq();
+ if (unlikely(irq < 0)) {
+ pr_err("no more free IRQs, bailing..\n");
+ break;
+ }
+
+ pr_info("Setting up a chained VIRQ from %d -> %d\n",
+ irq, entry->pirq);
+
+ intc_irq_xlate_set(irq, entry->enum_id, d);
+
+ set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq),
+ handle_simple_irq, "virq");
+ set_irq_chip_data(irq, get_irq_chip_data(entry->pirq));
+
+ set_irq_data(irq, (void *)entry->handle);
+
+ set_irq_chained_handler(entry->pirq, intc_virq_handler);
+ add_virq_to_pirq(entry->pirq, irq);
+
+ radix_tree_tag_clear(&d->tree, entry->enum_id,
+ INTC_TAG_VIRQ_NEEDS_ALLOC);
+ radix_tree_replace_slot((void **)entries[i],
+ &intc_irq_xlate[irq]);
+ }
+
+ raw_spin_unlock_irqrestore(&d->lock, flags);
+}
+
+void __init intc_finalize(void)
+{
+ struct intc_desc_int *d;
+
+ list_for_each_entry(d, &intc_list, list)
+ if (radix_tree_tagged(&d->tree, INTC_TAG_VIRQ_NEEDS_ALLOC))
+ intc_subgroup_map(d);
+}
diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c
index cf0303acab8e..75934e3ea34e 100644
--- a/drivers/sh/pfc.c
+++ b/drivers/sh/pfc.c
@@ -7,6 +7,8 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -559,10 +561,8 @@ static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio)
struct pinmux_data_reg *dr = NULL;
int bit = 0;
- if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) {
- BUG();
- return 0;
- }
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
+ return -EINVAL;
return gpio_read_reg(dr->reg, dr->reg_width, 1, bit);
}
@@ -581,7 +581,7 @@ int register_pinmux(struct pinmux_info *pip)
{
struct gpio_chip *chip = &pip->chip;
- pr_info("sh pinmux: %s handling gpio %d -> %d\n",
+ pr_info("%s handling gpio %d -> %d\n",
pip->name, pip->first_gpio, pip->last_gpio);
setup_data_regs(pip);
@@ -602,3 +602,10 @@ int register_pinmux(struct pinmux_info *pip)
return gpiochip_add(chip);
}
+
+int unregister_pinmux(struct pinmux_info *pip)
+{
+ pr_info("%s deregistering\n", pip->name);
+
+ return gpiochip_remove(&pip->chip);
+}
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4b9eec68fad6..78f9fd02c1b2 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -329,6 +329,13 @@ config SPI_STMP3XXX
help
SPI driver for Freescale STMP37xx/378x SoC SSP interface
+config SPI_TEGRA
+ tristate "Nvidia Tegra SPI controller"
+ depends on ARCH_TEGRA
+ select TEGRA_SYSTEM_DMA
+ help
+ SPI driver for NVidia Tegra SoCs
+
config SPI_TOPCLIFF_PCH
tristate "Topcliff PCH SPI Controller"
depends on PCI
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 557aaadf56b2..8bc1a5abac1f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o
+obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c
new file mode 100644
index 000000000000..bb7df02a5472
--- /dev/null
+++ b/drivers/spi/spi_tegra.c
@@ -0,0 +1,618 @@
+/*
+ * Driver for Nvidia TEGRA spi controller.
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Erik Gilling <konkers@android.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/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/spi/spi.h>
+
+#include <mach/dma.h>
+
+#define SLINK_COMMAND 0x000
+#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0)
+#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5)
+#define SLINK_BOTH_EN (1 << 10)
+#define SLINK_CS_SW (1 << 11)
+#define SLINK_CS_VALUE (1 << 12)
+#define SLINK_CS_POLARITY (1 << 13)
+#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16)
+#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16)
+#define SLINK_IDLE_SDA_PULL_LOW (2 << 16)
+#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16)
+#define SLINK_IDLE_SDA_MASK (3 << 16)
+#define SLINK_CS_POLARITY1 (1 << 20)
+#define SLINK_CK_SDA (1 << 21)
+#define SLINK_CS_POLARITY2 (1 << 22)
+#define SLINK_CS_POLARITY3 (1 << 23)
+#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24)
+#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24)
+#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24)
+#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24)
+#define SLINK_IDLE_SCLK_MASK (3 << 24)
+#define SLINK_M_S (1 << 28)
+#define SLINK_WAIT (1 << 29)
+#define SLINK_GO (1 << 30)
+#define SLINK_ENB (1 << 31)
+
+#define SLINK_COMMAND2 0x004
+#define SLINK_LSBFE (1 << 0)
+#define SLINK_SSOE (1 << 1)
+#define SLINK_SPIE (1 << 4)
+#define SLINK_BIDIROE (1 << 6)
+#define SLINK_MODFEN (1 << 7)
+#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8)
+#define SLINK_CS_ACTIVE_BETWEEN (1 << 17)
+#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18)
+#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20)
+#define SLINK_FIFO_REFILLS_0 (0 << 22)
+#define SLINK_FIFO_REFILLS_1 (1 << 22)
+#define SLINK_FIFO_REFILLS_2 (2 << 22)
+#define SLINK_FIFO_REFILLS_3 (3 << 22)
+#define SLINK_FIFO_REFILLS_MASK (3 << 22)
+#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26)
+#define SLINK_SPC0 (1 << 29)
+#define SLINK_TXEN (1 << 30)
+#define SLINK_RXEN (1 << 31)
+
+#define SLINK_STATUS 0x008
+#define SLINK_COUNT(val) (((val) >> 0) & 0x1f)
+#define SLINK_WORD(val) (((val) >> 5) & 0x1f)
+#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff)
+#define SLINK_MODF (1 << 16)
+#define SLINK_RX_UNF (1 << 18)
+#define SLINK_TX_OVF (1 << 19)
+#define SLINK_TX_FULL (1 << 20)
+#define SLINK_TX_EMPTY (1 << 21)
+#define SLINK_RX_FULL (1 << 22)
+#define SLINK_RX_EMPTY (1 << 23)
+#define SLINK_TX_UNF (1 << 24)
+#define SLINK_RX_OVF (1 << 25)
+#define SLINK_TX_FLUSH (1 << 26)
+#define SLINK_RX_FLUSH (1 << 27)
+#define SLINK_SCLK (1 << 28)
+#define SLINK_ERR (1 << 29)
+#define SLINK_RDY (1 << 30)
+#define SLINK_BSY (1 << 31)
+
+#define SLINK_MAS_DATA 0x010
+#define SLINK_SLAVE_DATA 0x014
+
+#define SLINK_DMA_CTL 0x018
+#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0)
+#define SLINK_TX_TRIG_1 (0 << 16)
+#define SLINK_TX_TRIG_4 (1 << 16)
+#define SLINK_TX_TRIG_8 (2 << 16)
+#define SLINK_TX_TRIG_16 (3 << 16)
+#define SLINK_TX_TRIG_MASK (3 << 16)
+#define SLINK_RX_TRIG_1 (0 << 18)
+#define SLINK_RX_TRIG_4 (1 << 18)
+#define SLINK_RX_TRIG_8 (2 << 18)
+#define SLINK_RX_TRIG_16 (3 << 18)
+#define SLINK_RX_TRIG_MASK (3 << 18)
+#define SLINK_PACKED (1 << 20)
+#define SLINK_PACK_SIZE_4 (0 << 21)
+#define SLINK_PACK_SIZE_8 (1 << 21)
+#define SLINK_PACK_SIZE_16 (2 << 21)
+#define SLINK_PACK_SIZE_32 (3 << 21)
+#define SLINK_PACK_SIZE_MASK (3 << 21)
+#define SLINK_IE_TXC (1 << 26)
+#define SLINK_IE_RXC (1 << 27)
+#define SLINK_DMA_EN (1 << 31)
+
+#define SLINK_STATUS2 0x01c
+#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0)
+#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f) >> 16)
+
+#define SLINK_TX_FIFO 0x100
+#define SLINK_RX_FIFO 0x180
+
+static const unsigned long spi_tegra_req_sels[] = {
+ TEGRA_DMA_REQ_SEL_SL2B1,
+ TEGRA_DMA_REQ_SEL_SL2B2,
+ TEGRA_DMA_REQ_SEL_SL2B3,
+ TEGRA_DMA_REQ_SEL_SL2B4,
+};
+
+#define BB_LEN 32
+
+struct spi_tegra_data {
+ struct spi_master *master;
+ struct platform_device *pdev;
+ spinlock_t lock;
+
+ struct clk *clk;
+ void __iomem *base;
+ unsigned long phys;
+
+ u32 cur_speed;
+
+ struct list_head queue;
+ struct spi_transfer *cur;
+ unsigned cur_pos;
+ unsigned cur_len;
+ unsigned cur_bytes_per_word;
+
+ /* The tegra spi controller has a bug which causes the first word
+ * in PIO transactions to be garbage. Since packed DMA transactions
+ * require transfers to be 4 byte aligned we need a bounce buffer
+ * for the generic case.
+ */
+ struct tegra_dma_req rx_dma_req;
+ struct tegra_dma_channel *rx_dma;
+ u32 *rx_bb;
+ dma_addr_t rx_bb_phys;
+};
+
+
+static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi,
+ unsigned long reg)
+{
+ return readl(tspi->base + reg);
+}
+
+static inline void spi_tegra_writel(struct spi_tegra_data *tspi,
+ unsigned long val,
+ unsigned long reg)
+{
+ writel(val, tspi->base + reg);
+}
+
+static void spi_tegra_go(struct spi_tegra_data *tspi)
+{
+ unsigned long val;
+
+ wmb();
+
+ val = spi_tegra_readl(tspi, SLINK_DMA_CTL);
+ val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN;
+ val |= SLINK_DMA_BLOCK_SIZE(tspi->rx_dma_req.size / 4 - 1);
+ spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
+
+ tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req);
+
+ val |= SLINK_DMA_EN;
+ spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
+}
+
+static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi,
+ struct spi_transfer *t)
+{
+ unsigned len = min(t->len - tspi->cur_pos, BB_LEN *
+ tspi->cur_bytes_per_word);
+ u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_pos;
+ int i, j;
+ unsigned long val;
+
+ val = spi_tegra_readl(tspi, SLINK_COMMAND);
+ val &= ~SLINK_WORD_SIZE(~0);
+ val |= SLINK_WORD_SIZE(len / tspi->cur_bytes_per_word - 1);
+ spi_tegra_writel(tspi, val, SLINK_COMMAND);
+
+ for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
+ val = 0;
+ for (j = 0; j < tspi->cur_bytes_per_word; j++)
+ val |= tx_buf[i + j] << j * 8;
+
+ spi_tegra_writel(tspi, val, SLINK_TX_FIFO);
+ }
+
+ tspi->rx_dma_req.size = len / tspi->cur_bytes_per_word * 4;
+
+ return len;
+}
+
+static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi,
+ struct spi_transfer *t)
+{
+ unsigned len = tspi->cur_len;
+ u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_pos;
+ int i, j;
+ unsigned long val;
+
+ for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
+ val = tspi->rx_bb[i / tspi->cur_bytes_per_word];
+ for (j = 0; j < tspi->cur_bytes_per_word; j++)
+ rx_buf[i + j] = (val >> (j * 8)) & 0xff;
+ }
+
+ return len;
+}
+
+static void spi_tegra_start_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
+ u32 speed;
+ u8 bits_per_word;
+ unsigned long val;
+
+ speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
+ bits_per_word = t->bits_per_word ? t->bits_per_word :
+ spi->bits_per_word;
+
+ tspi->cur_bytes_per_word = (bits_per_word - 1) / 8 + 1;
+
+ if (speed != tspi->cur_speed)
+ clk_set_rate(tspi->clk, speed);
+
+ if (tspi->cur_speed == 0)
+ clk_enable(tspi->clk);
+
+ tspi->cur_speed = speed;
+
+ val = spi_tegra_readl(tspi, SLINK_COMMAND2);
+ val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN;
+ if (t->rx_buf)
+ val |= SLINK_RXEN;
+ if (t->tx_buf)
+ val |= SLINK_TXEN;
+ val |= SLINK_SS_EN_CS(spi->chip_select);
+ val |= SLINK_SPIE;
+ spi_tegra_writel(tspi, val, SLINK_COMMAND2);
+
+ val = spi_tegra_readl(tspi, SLINK_COMMAND);
+ val &= ~SLINK_BIT_LENGTH(~0);
+ val |= SLINK_BIT_LENGTH(bits_per_word - 1);
+
+ /* FIXME: should probably control CS manually so that we can be sure
+ * it does not go low between transfer and to support delay_usecs
+ * correctly.
+ */
+ val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA & ~SLINK_CS_SW;
+
+ if (spi->mode & SPI_CPHA)
+ val |= SLINK_CK_SDA;
+
+ if (spi->mode & SPI_CPOL)
+ val |= SLINK_IDLE_SCLK_DRIVE_HIGH;
+ else
+ val |= SLINK_IDLE_SCLK_DRIVE_LOW;
+
+ val |= SLINK_M_S;
+
+ spi_tegra_writel(tspi, val, SLINK_COMMAND);
+
+ spi_tegra_writel(tspi, SLINK_RX_FLUSH | SLINK_TX_FLUSH, SLINK_STATUS);
+
+ tspi->cur = t;
+ tspi->cur_pos = 0;
+ tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, t);
+
+ spi_tegra_go(tspi);
+}
+
+static void spi_tegra_start_message(struct spi_device *spi,
+ struct spi_message *m)
+{
+ struct spi_transfer *t;
+
+ m->actual_length = 0;
+ m->status = 0;
+
+ t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list);
+ spi_tegra_start_transfer(spi, t);
+}
+
+static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req)
+{
+ struct spi_tegra_data *tspi = req->dev;
+ unsigned long flags;
+ struct spi_message *m;
+ struct spi_device *spi;
+ int timeout = 0;
+ unsigned long val;
+
+ /* the SPI controller may come back with both the BSY and RDY bits
+ * set. In this case we need to wait for the BSY bit to clear so
+ * that we are sure the DMA is finished. 1000 reads was empirically
+ * determined to be long enough.
+ */
+ while (timeout++ < 1000) {
+ if (!(spi_tegra_readl(tspi, SLINK_STATUS) & SLINK_BSY))
+ break;
+ }
+
+ spin_lock_irqsave(&tspi->lock, flags);
+
+ val = spi_tegra_readl(tspi, SLINK_STATUS);
+ val |= SLINK_RDY;
+ spi_tegra_writel(tspi, val, SLINK_STATUS);
+
+ m = list_first_entry(&tspi->queue, struct spi_message, queue);
+
+ if (timeout >= 1000)
+ m->status = -EIO;
+
+ spi = m->state;
+
+ tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur);
+ m->actual_length += tspi->cur_pos;
+
+ if (tspi->cur_pos < tspi->cur->len) {
+ tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur);
+ spi_tegra_go(tspi);
+ } else if (!list_is_last(&tspi->cur->transfer_list,
+ &m->transfers)) {
+ tspi->cur = list_first_entry(&tspi->cur->transfer_list,
+ struct spi_transfer,
+ transfer_list);
+ spi_tegra_start_transfer(spi, tspi->cur);
+ } else {
+ list_del(&m->queue);
+
+ m->complete(m->context);
+
+ if (!list_empty(&tspi->queue)) {
+ m = list_first_entry(&tspi->queue, struct spi_message,
+ queue);
+ spi = m->state;
+ spi_tegra_start_message(spi, m);
+ } else {
+ clk_disable(tspi->clk);
+ tspi->cur_speed = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&tspi->lock, flags);
+}
+
+static int spi_tegra_setup(struct spi_device *spi)
+{
+ struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
+ unsigned long cs_bit;
+ unsigned long val;
+ unsigned long flags;
+
+ dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n",
+ spi->bits_per_word,
+ spi->mode & SPI_CPOL ? "" : "~",
+ spi->mode & SPI_CPHA ? "" : "~",
+ spi->max_speed_hz);
+
+
+ switch (spi->chip_select) {
+ case 0:
+ cs_bit = SLINK_CS_POLARITY;
+ break;
+
+ case 1:
+ cs_bit = SLINK_CS_POLARITY1;
+ break;
+
+ case 2:
+ cs_bit = SLINK_CS_POLARITY2;
+ break;
+
+ case 4:
+ cs_bit = SLINK_CS_POLARITY3;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&tspi->lock, flags);
+
+ val = spi_tegra_readl(tspi, SLINK_COMMAND);
+ if (spi->mode & SPI_CS_HIGH)
+ val |= cs_bit;
+ else
+ val &= ~cs_bit;
+ spi_tegra_writel(tspi, val, SLINK_COMMAND);
+
+ spin_unlock_irqrestore(&tspi->lock, flags);
+
+ return 0;
+}
+
+static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m)
+{
+ struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
+ struct spi_transfer *t;
+ unsigned long flags;
+ int was_empty;
+
+ if (list_empty(&m->transfers) || !m->complete)
+ return -EINVAL;
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->bits_per_word < 0 || t->bits_per_word > 32)
+ return -EINVAL;
+
+ if (t->len == 0)
+ return -EINVAL;
+
+ if (!t->rx_buf && !t->tx_buf)
+ return -EINVAL;
+ }
+
+ m->state = spi;
+
+ spin_lock_irqsave(&tspi->lock, flags);
+ was_empty = list_empty(&tspi->queue);
+ list_add_tail(&m->queue, &tspi->queue);
+
+ if (was_empty)
+ spi_tegra_start_message(spi, m);
+
+ spin_unlock_irqrestore(&tspi->lock, flags);
+
+ return 0;
+}
+
+static int __init spi_tegra_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct spi_tegra_data *tspi;
+ struct resource *r;
+ int ret;
+
+ master = spi_alloc_master(&pdev->dev, sizeof *tspi);
+ if (master == NULL) {
+ dev_err(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* the spi->mode bits understood by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+
+ master->bus_num = pdev->id;
+
+ master->setup = spi_tegra_setup;
+ master->transfer = spi_tegra_transfer;
+ master->num_chipselect = 4;
+
+ dev_set_drvdata(&pdev->dev, master);
+ tspi = spi_master_get_devdata(master);
+ tspi->master = master;
+ tspi->pdev = pdev;
+ spin_lock_init(&tspi->lock);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ ret = -ENODEV;
+ goto err0;
+ }
+
+ if (!request_mem_region(r->start, (r->end - r->start) + 1,
+ dev_name(&pdev->dev))) {
+ ret = -EBUSY;
+ goto err0;
+ }
+
+ tspi->phys = r->start;
+ tspi->base = ioremap(r->start, r->end - r->start + 1);
+ if (!tspi->base) {
+ dev_err(&pdev->dev, "can't ioremap iomem\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ tspi->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR_OR_NULL(tspi->clk)) {
+ dev_err(&pdev->dev, "can not get clock\n");
+ ret = PTR_ERR(tspi->clk);
+ goto err2;
+ }
+
+ INIT_LIST_HEAD(&tspi->queue);
+
+ tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+ if (!tspi->rx_dma) {
+ dev_err(&pdev->dev, "can not allocate rx dma channel\n");
+ ret = -ENODEV;
+ goto err3;
+ }
+
+ tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
+ &tspi->rx_bb_phys, GFP_KERNEL);
+ if (!tspi->rx_bb) {
+ dev_err(&pdev->dev, "can not allocate rx bounce buffer\n");
+ ret = -ENOMEM;
+ goto err4;
+ }
+
+ tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete;
+ tspi->rx_dma_req.to_memory = 1;
+ tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys;
+ tspi->rx_dma_req.dest_bus_width = 32;
+ tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO;
+ tspi->rx_dma_req.source_bus_width = 32;
+ tspi->rx_dma_req.source_wrap = 4;
+ tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id];
+ tspi->rx_dma_req.dev = tspi;
+
+ ret = spi_register_master(master);
+
+ if (ret < 0)
+ goto err5;
+
+ return ret;
+
+err5:
+ dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
+ tspi->rx_bb, tspi->rx_bb_phys);
+err4:
+ tegra_dma_free_channel(tspi->rx_dma);
+err3:
+ clk_put(tspi->clk);
+err2:
+ iounmap(tspi->base);
+err1:
+ release_mem_region(r->start, (r->end - r->start) + 1);
+err0:
+ spi_master_put(master);
+ return ret;
+}
+
+static int __devexit spi_tegra_remove(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct spi_tegra_data *tspi;
+ struct resource *r;
+
+ master = dev_get_drvdata(&pdev->dev);
+ tspi = spi_master_get_devdata(master);
+
+ tegra_dma_free_channel(tspi->rx_dma);
+
+ dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
+ tspi->rx_bb, tspi->rx_bb_phys);
+
+ clk_put(tspi->clk);
+ iounmap(tspi->base);
+
+ spi_master_put(master);
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(r->start, (r->end - r->start) + 1);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:spi_tegra");
+
+static struct platform_driver spi_tegra_driver = {
+ .driver = {
+ .name = "spi_tegra",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(spi_tegra_remove),
+};
+
+static int __init spi_tegra_init(void)
+{
+ return platform_driver_probe(&spi_tegra_driver, spi_tegra_probe);
+}
+module_init(spi_tegra_init);
+
+static void __exit spi_tegra_exit(void)
+{
+ platform_driver_unregister(&spi_tegra_driver);
+}
+module_exit(spi_tegra_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 335311a98fdc..8e03e7600239 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -139,8 +139,6 @@ source "drivers/staging/adis16255/Kconfig"
source "drivers/staging/xgifb/Kconfig"
-source "drivers/staging/mrst-touchscreen/Kconfig"
-
source "drivers/staging/msm/Kconfig"
source "drivers/staging/lirc/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index e3f1e1b6095e..0e7d7559d379 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -51,7 +51,6 @@ obj-$(CONFIG_CXT1E1) += cxt1e1/
obj-$(CONFIG_TI_ST) += ti-st/
obj-$(CONFIG_ADIS16255) += adis16255/
obj-$(CONFIG_FB_XGI) += xgifb/
-obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/
obj-$(CONFIG_MSM_STAGING) += msm/
obj-$(CONFIG_EASYCAP) += easycap/
obj-$(CONFIG_SOLO6X10) += solo6x10/
diff --git a/drivers/staging/mrst-touchscreen/Kconfig b/drivers/staging/mrst-touchscreen/Kconfig
deleted file mode 100644
index c2af49217084..000000000000
--- a/drivers/staging/mrst-touchscreen/Kconfig
+++ /dev/null
@@ -1,7 +0,0 @@
-config TOUCHSCREEN_INTEL_MID
- tristate "Intel MID platform resistive touchscreen"
- depends on INTEL_SCU_IPC
- default y
- help
- Say Y here if you have a Intel MID based touchscreen
- If unsure, say N.
diff --git a/drivers/staging/mrst-touchscreen/Makefile b/drivers/staging/mrst-touchscreen/Makefile
deleted file mode 100644
index 2d638b0d70bf..000000000000
--- a/drivers/staging/mrst-touchscreen/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) := intel_mid_touch.o
-
-
diff --git a/drivers/staging/mrst-touchscreen/TODO b/drivers/staging/mrst-touchscreen/TODO
deleted file mode 100644
index 7157028d634a..000000000000
--- a/drivers/staging/mrst-touchscreen/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-- Move the driver to not think it is SPI (requires fixing some of the SFI
- and firmware side)
diff --git a/drivers/staging/mrst-touchscreen/intel-mid-touch.c b/drivers/staging/mrst-touchscreen/intel-mid-touch.c
deleted file mode 100644
index abba22f921be..000000000000
--- a/drivers/staging/mrst-touchscreen/intel-mid-touch.c
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * intel_mid_touch.c - Intel MID Resistive Touch Screen Driver
- *
- * Copyright (C) 2008 Intel Corp
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; ifnot, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
- * Ramesh Agarwal (ramesh.agarwal@intel.com)
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * TODO:
- * kill off mrstouch_debug eventually
- * review conversion of r/m/w sequences
- * Replace interrupt mutex abuse
- * Kill of mrstouchdevp pointer
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/input.h>
-#include <linux/interrupt.h>
-#include <linux/err.h>
-#include <linux/param.h>
-#include <linux/spi/spi.h>
-#include <linux/irq.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <asm/intel_scu_ipc.h>
-
-
-#if defined(MRSTOUCH_DEBUG)
-#define mrstouch_debug(fmt, args...)\
- do { \
- printk(KERN_DEBUG "\n[MRSTOUCH(%d)] - ", __LINE__); \
- printk(KERN_DEBUG fmt, ##args); \
- } while (0);
-#else
-#define mrstouch_debug(fmt, args...)
-#endif
-
-/* PMIC Interrupt registers */
-#define PMIC_REG_ID1 0x00 /*PMIC ID1 register */
-
-/* PMIC Interrupt registers */
-#define PMIC_REG_INT 0x04 /*PMIC interrupt register */
-#define PMIC_REG_MINT 0x05 /*PMIC interrupt mask register */
-
-/* ADC Interrupt registers */
-#define PMIC_REG_ADCINT 0x5F /*ADC interrupt register */
-#define PMIC_REG_MADCINT 0x60 /*ADC interrupt mask register */
-
-/* ADC Control registers */
-#define PMIC_REG_ADCCNTL1 0x61 /*ADC control register */
-
-/* ADC Channel Selection registers */
-#define PMICADDR0 0xA4
-#define END_OF_CHANNEL 0x1F
-
-/* ADC Result register */
-#define PMIC_REG_ADCSNS0H 0x64
-
-/* ADC channels for touch screen */
-#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
-#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
-#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
-#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
-
-/* Touch screen coordinate constants */
-#define TOUCH_PRESSURE 50
-#define TOUCH_PRESSURE_FS 100
-
-#define XMOVE_LIMIT 5
-#define YMOVE_LIMIT 5
-#define XYMOVE_CNT 3
-
-#define MAX_10BIT ((1<<10)-1)
-
-/* Touch screen channel BIAS constants */
-#define XBIAS 0x20
-#define YBIAS 0x40
-#define ZBIAS 0x80
-
-/* Touch screen coordinates */
-#define MIN_X 10
-#define MAX_X 1024
-#define MIN_Y 10
-#define MAX_Y 1024
-#define WAIT_ADC_COMPLETION 10
-
-/* PMIC ADC round robin delays */
-#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
-#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
-
-/* PMIC Vendor Identifiers */
-#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
-#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
-#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
-#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
-
-/* Touch screen device structure */
-struct mrstouch_dev {
- struct spi_device *spi; /* SPI device associated with touch screen */
- struct input_dev *input; /* input device for touchscreen*/
- char phys[32]; /* Device name */
- struct task_struct *pendet_thrd; /* PENDET interrupt handler */
- struct mutex lock; /* Sync between interrupt and PENDET handler */
- bool busy; /* Busy flag */
- u16 asr; /* Address selection register */
- int irq; /* Touch screen IRQ # */
- uint vendor; /* PMIC vendor */
- uint rev; /* PMIC revision */
- bool suspended; /* Device suspended status */
- bool disabled; /* Device disabled status */
- u16 x; /* X coordinate */
- u16 y; /* Y coordinate */
- bool pendown; /* PEN position */
-} ;
-
-
-/* Global Pointer to Touch screen device */
-static struct mrstouch_dev *mrstouchdevp;
-
-/* Utility to read PMIC ID */
-static int mrstouch_pmic_id(uint *vendor, uint *rev)
-{
- int err;
- u8 r;
-
- err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
- if (err)
- return err;
-
- *vendor = r & 0x7;
- *rev = (r >> 3) & 0x7;
-
- return 0;
-}
-
-/*
- * Parse ADC channels to find end of the channel configured by other ADC user
- * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
- */
-static int mrstouch_chan_parse(struct mrstouch_dev *tsdev)
-{
- int err, i, j, found;
- u32 r32;
-
- found = -1;
-
- for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
- if (found >= 0)
- break;
-
- err = intel_scu_ipc_ioread32(PMICADDR0, &r32);
- if (err)
- return err;
-
- for (j = 0; j < 32; j+= 8) {
- if (((r32 >> j) & 0xFF) == END_OF_CHANNEL) {
- found = i;
- break;
- }
- }
- }
- if (found < 0)
- return 0;
-
- if (tsdev->vendor == PMIC_VENDOR_FS) {
- if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
- return -ENOSPC;
- } else {
- if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
- return -ENOSPC;
- }
- return found;
-}
-
-/* Utility to enable/disable pendet.
- * pendet set to true enables PENDET interrupt
- * pendet set to false disables PENDET interrupt
- * Also clears RND mask bit
-*/
-static int pendet_enable(struct mrstouch_dev *tsdev, bool pendet)
-{
- u16 reg;
- u8 r;
- u8 pendet_enabled = 0;
- int retry = 0;
- int err;
-
- err = intel_scu_ipc_ioread16(PMIC_REG_MADCINT, &reg);
- if (err)
- return err;
-
- if (pendet) {
- reg &= ~0x0005;
- reg |= 0x2000; /* Enable pendet */
- } else
- reg &= 0xDFFF; /* Disable pendet */
-
- /* Set MADCINT and update ADCCNTL1 (next reg byte) */
- err = intel_scu_ipc_iowrite16(PMIC_REG_MADCINT, reg);
- if (!pendet || err)
- return err;
-
- /*
- * Sometimes even after the register write succeeds
- * the PMIC register value is not updated. Retry few iterations
- * to enable pendet.
- */
-
- err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r);
- pendet_enabled = (r >> 5) & 0x01;
-
- retry = 0;
- while (!err && !pendet_enabled) {
- retry++;
- msleep(10);
- err = intel_scu_ipc_iowrite8(PMIC_REG_ADCCNTL1, reg >> 8);
- if (err)
- break;
- err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r);
- if (err == 0)
- pendet_enabled = (r >> 5) & 0x01;
- if (retry >= 10) {
- dev_err(&tsdev->spi->dev, "Touch screen disabled.\n");
- return -EIO;
- }
- }
- return 0;
-}
-
-/* To read PMIC ADC touch screen result
- * Reads ADC storage registers for higher 7 and lower 3 bits
- * converts the two readings to single value and turns off gain bit
- */
-static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
-{
- int err;
- u16 result;
- u32 res;
-
- result = PMIC_REG_ADCSNS0H + offset;
-
- if (chan == MRST_TS_CHAN12)
- result += 4;
-
- err = intel_scu_ipc_ioread32(result, &res);
- if (err)
- return err;
-
- /* Mash the bits up */
-
- *vp = (res & 0xFF) << 3; /* Highest 7 bits */
- *vp |= (res >> 8) & 0x07; /* Lower 3 bits */
- *vp &= 0x3FF;
-
- res >>= 16;
-
- *vm = (res & 0xFF) << 3; /* Highest 7 bits */
- *vm |= (res >> 8) & 0x07; /* Lower 3 bits */
- *vm &= 0x3FF;
-
- return 0;
-}
-
-/* To configure touch screen channels
- * Writes touch screen channels to ADC address selection registers
- */
-static int mrstouch_ts_chan_set(uint offset)
-{
- int count;
- u16 chan;
- u16 reg[5];
- u8 data[5];
-
- chan = PMICADDR0 + offset;
- for (count = 0; count <= 3; count++) {
- reg[count] = chan++;
- data[count] = MRST_TS_CHAN10 + count;
- }
- reg[count] = chan;
- data[count] = END_OF_CHANNEL;
-
- return intel_scu_ipc_writev(reg, data, 5);
-}
-
-/* Initialize ADC */
-static int mrstouch_adc_init(struct mrstouch_dev *tsdev)
-{
- int err, start;
- u8 ra, rm;
-
- err = mrstouch_pmic_id(&tsdev->vendor, &tsdev->rev);
- if (err) {
- dev_err(&tsdev->spi->dev, "Unable to read PMIC id\n");
- return err;
- }
-
- start = mrstouch_chan_parse(tsdev);
- if (start < 0) {
- dev_err(&tsdev->spi->dev, "Unable to parse channels\n");
- return start;
- }
-
- tsdev->asr = start;
-
- mrstouch_debug("Channel offset(%d): 0x%X\n", tsdev->asr, tsdev->vendor);
-
- /* ADC power on, start, enable PENDET and set loop delay
- * ADC loop delay is set to 4.5 ms approximately
- * Loop delay more than this results in jitter in adc readings
- * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET
- * interrupt generation sometimes.
- */
-
- if (tsdev->vendor == PMIC_VENDOR_FS) {
- ra = 0xE0 | ADC_LOOP_DELAY0;
- rm = 0x5;
- } else {
- /* NEC and MAXIm not consistent with loop delay 0 */
- ra = 0xE0 | ADC_LOOP_DELAY1;
- rm = 0x0;
-
- /* configure touch screen channels */
- err = mrstouch_ts_chan_set(tsdev->asr);
- if (err)
- return err;
- }
- err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
- if (err == 0)
- err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
- return err;
-}
-
-/* Reports x,y coordinates to event subsystem */
-static void mrstouch_report_xy(struct mrstouch_dev *tsdev, u16 x, u16 y, u16 z)
-{
- int xdiff, ydiff;
-
- if (tsdev->pendown && z <= TOUCH_PRESSURE) {
- /* Pen removed, report button release */
- mrstouch_debug("BTN REL(%d)", z);
- input_report_key(tsdev->input, BTN_TOUCH, 0);
- tsdev->pendown = false;
- }
-
- xdiff = abs(x - tsdev->x);
- ydiff = abs(y - tsdev->y);
-
- /*
- if x and y values changes for XYMOVE_CNT readings it is considered
- as stylus is moving. This is required to differentiate between stylus
- movement and jitter
- */
- if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_Y) {
- /* Spurious values, release button if touched and return */
- if (tsdev->pendown) {
- mrstouch_debug("BTN REL(%d)", z);
- input_report_key(tsdev->input, BTN_TOUCH, 0);
- tsdev->pendown = false;
- }
- return;
- } else if (xdiff >= XMOVE_LIMIT || ydiff >= YMOVE_LIMIT) {
- tsdev->x = x;
- tsdev->y = y;
-
- input_report_abs(tsdev->input, ABS_X, x);
- input_report_abs(tsdev->input, ABS_Y, y);
- input_sync(tsdev->input);
- }
-
-
- if (!tsdev->pendown && z > TOUCH_PRESSURE) {
- /* Pen touched, report button touch */
- mrstouch_debug("BTN TCH(%d, %d, %d)", x, y, z);
- input_report_key(tsdev->input, BTN_TOUCH, 1);
- tsdev->pendown = true;
- }
-}
-
-
-/* Utility to start ADC, used by freescale handler */
-static int pendet_mask(void)
-{
- return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
-}
-
-/* Utility to stop ADC, used by freescale handler */
-static int pendet_umask(void)
-{
- return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
-}
-
-/* Utility to read ADC, used by freescale handler */
-static int mrstouch_pmic_fs_adc_read(struct mrstouch_dev *tsdev)
-{
- int err;
- u16 x, y, z, result;
- u16 reg[4];
- u8 data[4];
-
- result = PMIC_REG_ADCSNS0H + tsdev->asr;
-
- reg[0] = result + 4;
- reg[1] = result + 5;
- reg[2] = result + 16;
- reg[3] = result + 17;
-
- err = intel_scu_ipc_readv(reg, data, 4);
- if (err)
- goto ipc_error;
-
- x = data[0] << 3; /* Higher 7 bits */
- x |= data[1] & 0x7; /* Lower 3 bits */
- x &= 0x3FF;
-
- y = data[2] << 3; /* Higher 7 bits */
- y |= data[3] & 0x7; /* Lower 3 bits */
- y &= 0x3FF;
-
- /* Read Z value */
- reg[0] = result + 28;
- reg[1] = result + 29;
-
- err = intel_scu_ipc_readv(reg, data, 4);
- if (err)
- goto ipc_error;
-
- z = data[0] << 3; /* Higher 7 bits */
- z |= data[1] & 0x7; /* Lower 3 bits */
- z &= 0x3FF;
-
-#if defined(MRSTOUCH_PRINT_XYZP)
- mrstouch_debug("X: %d, Y: %d, Z: %d", x, y, z);
-#endif
-
- if (z >= TOUCH_PRESSURE_FS) {
- mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE - 1); /* Pen Removed */
- return TOUCH_PRESSURE - 1;
- } else {
- mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE + 1); /* Pen Touched */
- return TOUCH_PRESSURE + 1;
- }
-
- return 0;
-
-ipc_error:
- dev_err(&tsdev->spi->dev, "ipc error during fs_adc read\n");
- return err;
-}
-
-/* To handle free scale pmic pendet interrupt */
-static int pmic0_pendet(void *dev_id)
-{
- int err, count;
- u16 chan;
- unsigned int touched;
- struct mrstouch_dev *tsdev = (struct mrstouch_dev *)dev_id;
- u16 reg[5];
- u8 data[5];
-
- chan = PMICADDR0 + tsdev->asr;
-
- /* Set X BIAS */
- for (count = 0; count <= 3; count++) {
- reg[count] = chan++;
- data[count] = 0x2A;
- }
- reg[count] = chan++; /* Dummy */
- data[count] = 0;
-
- err = intel_scu_ipc_writev(reg, data, 5);
- if (err)
- goto ipc_error;
-
- msleep(WAIT_ADC_COMPLETION);
-
- /* Set Y BIAS */
- for (count = 0; count <= 3; count++) {
- reg[count] = chan++;
- data[count] = 0x4A;
- }
- reg[count] = chan++; /* Dummy */
- data[count] = 0;
-
- err = intel_scu_ipc_writev(reg, data, 5);
- if (err)
- goto ipc_error;
-
- msleep(WAIT_ADC_COMPLETION);
-
- /* Set Z BIAS */
- err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
- if (err)
- goto ipc_error;
-
- msleep(WAIT_ADC_COMPLETION);
-
- /*Read touch screen channels till pen removed
- * Freescale reports constant value of z for all points
- * z is high when screen is not touched and low when touched
- * Map high z value to not touched and low z value to pen touched
- */
- touched = mrstouch_pmic_fs_adc_read(tsdev);
- while (touched > TOUCH_PRESSURE) {
- touched = mrstouch_pmic_fs_adc_read(tsdev);
- msleep(WAIT_ADC_COMPLETION);
- }
-
- /* Clear all TS channels */
- chan = PMICADDR0 + tsdev->asr;
- for (count = 0; count <= 4; count++) {
- reg[count] = chan++;
- data[count] = 0;
- }
- err = intel_scu_ipc_writev(reg, data, 5);
- if (err)
- goto ipc_error;
-
- for (count = 0; count <= 4; count++) {
- reg[count] = chan++;
- data[count] = 0;
- }
- err = intel_scu_ipc_writev(reg, data, 5);
- if (err)
- goto ipc_error;
-
- err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
- if (err)
- goto ipc_error;
-
- return 0;
-
-ipc_error:
- dev_err(&tsdev->spi->dev, "ipc error during pendet\n");
- return err;
-}
-
-
-/* To enable X, Y and Z bias values
- * Enables YPYM for X channels and XPXM for Y channels
- */
-static int mrstouch_ts_bias_set(uint offset, uint bias)
-{
- int count;
- u16 chan, start;
- u16 reg[4];
- u8 data[4];
-
- chan = PMICADDR0 + offset;
- start = MRST_TS_CHAN10;
-
- for (count = 0; count <= 3; count++) {
- reg[count] = chan++;
- data[count] = bias | (start + count);
- }
- return intel_scu_ipc_writev(reg, data, 4);
-}
-
-/* To read touch screen channel values */
-static int mrstouch_adc_read(struct mrstouch_dev *tsdev)
-{
- int err;
- u16 xp, xm, yp, ym, zp, zm;
-
- /* configure Y bias for X channels */
- err = mrstouch_ts_bias_set(tsdev->asr, YBIAS);
- if (err)
- goto ipc_error;
-
- msleep(WAIT_ADC_COMPLETION);
-
- /* read x+ and x- channels */
- err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &xp, &xm);
- if (err)
- goto ipc_error;
-
- /* configure x bias for y channels */
- err = mrstouch_ts_bias_set(tsdev->asr, XBIAS);
- if (err)
- goto ipc_error;
-
- msleep(WAIT_ADC_COMPLETION);
-
- /* read y+ and y- channels */
- err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, &yp, &ym);
- if (err)
- goto ipc_error;
-
- /* configure z bias for x and y channels */
- err = mrstouch_ts_bias_set(tsdev->asr, ZBIAS);
- if (err)
- goto ipc_error;
-
- msleep(WAIT_ADC_COMPLETION);
-
- /* read z+ and z- channels */
- err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &zp, &zm);
- if (err)
- goto ipc_error;
-
-#if defined(MRSTOUCH_PRINT_XYZP)
- printk(KERN_INFO "X+: %d, Y+: %d, Z+: %d\n", xp, yp, zp);
-#endif
-
-#if defined(MRSTOUCH_PRINT_XYZM)
- printk(KERN_INFO "X-: %d, Y-: %d, Z-: %d\n", xm, ym, zm);
-#endif
-
- mrstouch_report_xy(tsdev, xp, yp, zp); /* report x and y to eventX */
-
- return zp;
-
-ipc_error:
- dev_err(&tsdev->spi->dev, "ipc error during adc read\n");
- return err;
-}
-
-/* PENDET interrupt handler function for NEC and MAXIM */
-static void pmic12_pendet(void *data)
-{
- unsigned int touched;
- struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data;
-
- /* read touch screen channels till pen removed */
- do {
- touched = mrstouch_adc_read(tsdev);
- } while (touched > TOUCH_PRESSURE);
-}
-
-/* Handler to process PENDET interrupt */
-int mrstouch_pendet(void *data)
-{
- struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data;
- while (1) {
- /* Wait for PENDET interrupt */
- if (mutex_lock_interruptible(&tsdev->lock)) {
- msleep(WAIT_ADC_COMPLETION);
- continue;
- }
-
- if (tsdev->busy)
- return 0;
-
- tsdev->busy = true;
-
- if (tsdev->vendor == PMIC_VENDOR_NEC ||
- tsdev->vendor == PMIC_VENDOR_MAXIM) {
- /* PENDET must be disabled in NEC before reading ADC */
- pendet_enable(tsdev,false); /* Disbale PENDET */
- pmic12_pendet(tsdev);
- pendet_enable(tsdev, true); /*Enable PENDET */
- } else if (tsdev->vendor == PMIC_VENDOR_FS) {
- pendet_umask(); /* Stop ADC */
- pmic0_pendet(tsdev);
- pendet_mask(); /* Stop ADC */
- } else
- dev_err(&tsdev->spi->dev, "Unsupported touchscreen: %d\n",
- tsdev->vendor);
-
- tsdev->busy = false;
-
- }
- return 0;
-}
-
-/* PENDET interrupt handler */
-static irqreturn_t pendet_intr_handler(int irq, void *handle)
-{
- struct mrstouch_dev *tsdev = (struct mrstouch_dev *)handle;
-
- mutex_unlock(&tsdev->lock);
- return IRQ_HANDLED;
-}
-
-/* Intializes input device and registers with input subsystem */
-static int ts_input_dev_init(struct mrstouch_dev *tsdev, struct spi_device *spi)
-{
- int err = 0;
-
- mrstouch_debug("%s", __func__);
-
- tsdev->input = input_allocate_device();
- if (!tsdev->input) {
- dev_err(&tsdev->spi->dev, "Unable to allocate input device.\n");
- return -EINVAL;
- }
-
- tsdev->input->name = "mrst_touchscreen";
- snprintf(tsdev->phys, sizeof(tsdev->phys),
- "%s/input0", dev_name(&spi->dev));
- tsdev->input->phys = tsdev->phys;
- tsdev->input->dev.parent = &spi->dev;
-
- tsdev->input->id.vendor = tsdev->vendor;
- tsdev->input->id.version = tsdev->rev;
-
- tsdev->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- tsdev->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-
- input_set_abs_params(tsdev->input, ABS_X, MIN_X, MIN_Y, 0, 0);
- input_set_abs_params(tsdev->input, ABS_Y, MIN_X, MIN_Y, 0, 0);
-
- err = input_register_device(tsdev->input);
- if (err) {
- dev_err(&tsdev->spi->dev, "unable to register input device\n");
- input_free_device(tsdev->input);
- return err;
- }
-
- mrstouch_debug("%s", "mrstouch initialized");
-
- return 0;
-
-}
-
-/* Probe function for touch screen driver */
-static int __devinit mrstouch_probe(struct spi_device *mrstouch_spi)
-{
- int err;
- unsigned int myirq;
- struct mrstouch_dev *tsdev;
-
- mrstouch_debug("%s(%p)", __func__, mrstouch_spi);
-
- mrstouchdevp = NULL;
- myirq = mrstouch_spi->irq;
-
- if (!mrstouch_spi->irq) {
- dev_err(&mrstouch_spi->dev, "no interrupt assigned\n");
- return -EINVAL;
- }
-
- tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
- if (!tsdev) {
- dev_err(&mrstouch_spi->dev, "unable to allocate memory\n");
- return -ENOMEM;
- }
-
- tsdev->irq = myirq;
- mrstouchdevp = tsdev;
-
- err = mrstouch_adc_init(tsdev);
- if (err) {
- dev_err(&mrstouch_spi->dev, "ADC init failed\n");
- goto mrstouch_err_free_mem;
- }
-
- dev_set_drvdata(&mrstouch_spi->dev, tsdev);
- tsdev->spi = mrstouch_spi;
-
- err = ts_input_dev_init(tsdev, mrstouch_spi);
- if (err) {
- dev_err(&tsdev->spi->dev, "ts_input_dev_init failed");
- goto mrstouch_err_free_mem;
- }
-
- mutex_init(&tsdev->lock);
- mutex_lock(&tsdev->lock)
-
- mrstouch_debug("Requesting IRQ-%d", myirq);
- err = request_irq(myirq, pendet_intr_handler,
- 0, "mrstouch", tsdev);
- if (err) {
- dev_err(&tsdev->spi->dev, "unable to allocate irq\n");
- goto mrstouch_err_free_mem;
- }
-
- tsdev->pendet_thrd = kthread_run(mrstouch_pendet,
- (void *)tsdev, "pendet handler");
- if (IS_ERR(tsdev->pendet_thrd)) {
- dev_err(&tsdev->spi->dev, "kthread_run failed\n");
- err = PTR_ERR(tsdev->pendet_thrd);
- goto mrstouch_err_free_mem;
- }
- mrstouch_debug("%s", "Driver initialized");
- return 0;
-
-mrstouch_err_free_mem:
- kfree(tsdev);
- return err;
-}
-
-static int mrstouch_suspend(struct spi_device *spi, pm_message_t msg)
-{
- mrstouch_debug("%s", __func__);
- mrstouchdevp->suspended = 1;
- return 0;
-}
-
-static int mrstouch_resume(struct spi_device *spi)
-{
- mrstouch_debug("%s", __func__);
- mrstouchdevp->suspended = 0;
- return 0;
-}
-
-static int mrstouch_remove(struct spi_device *spi)
-{
- mrstouch_debug("%s", __func__);
- free_irq(mrstouchdevp->irq, mrstouchdevp);
- input_unregister_device(mrstouchdevp->input);
- input_free_device(mrstouchdevp->input);
- if (mrstouchdevp->pendet_thrd)
- kthread_stop(mrstouchdevp->pendet_thrd);
- kfree(mrstouchdevp);
- return 0;
-}
-
-static struct spi_driver mrstouch_driver = {
- .driver = {
- .name = "pmic_touch",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = mrstouch_probe,
- .suspend = mrstouch_suspend,
- .resume = mrstouch_resume,
- .remove = mrstouch_remove,
-};
-
-static int __init mrstouch_module_init(void)
-{
- int err;
-
- mrstouch_debug("%s", __func__);
- err = spi_register_driver(&mrstouch_driver);
- if (err) {
- mrstouch_debug("%s(%d)", "SPI PENDET failed", err);
- return -1;
- }
-
- return 0;
-}
-
-static void __exit mrstouch_module_exit(void)
-{
- mrstouch_debug("%s", __func__);
- spi_unregister_driver(&mrstouch_driver);
- return;
-}
-
-module_init(mrstouch_module_init);
-module_exit(mrstouch_module_exit);
-
-MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
-MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c
index 97dae297ca3c..c62d30017c07 100644
--- a/drivers/staging/pohmelfs/inode.c
+++ b/drivers/staging/pohmelfs/inode.c
@@ -882,12 +882,8 @@ static struct inode *pohmelfs_alloc_inode(struct super_block *sb)
static int pohmelfs_fsync(struct file *file, int datasync)
{
struct inode *inode = file->f_mapping->host;
- struct writeback_control wbc = {
- .sync_mode = WB_SYNC_ALL,
- .nr_to_write = 0, /* sys_fsync did this */
- };
- return sync_inode(inode, &wbc);
+ return sync_inode_metadata(inode, 1);
}
ssize_t pohmelfs_write(struct file *file, const char __user *buf,
diff --git a/drivers/staging/xgifb/TODO b/drivers/staging/xgifb/TODO
index 7d71019b84c2..c85ff5e9e700 100644
--- a/drivers/staging/xgifb/TODO
+++ b/drivers/staging/xgifb/TODO
@@ -12,4 +12,4 @@ TODO:
- get rid of non-linux related stuff
Please send patches to:
-Arnaud Patard <apatard@mandriva.com>
+Arnaud Patard <arnaud.patard@rtp-net.org>
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 095fa5366690..e2f63c0ea09d 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -276,6 +276,7 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de
struct inode *inode = new_inode(sb);
if (inode) {
+ inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_uid = current_fsuid();
inode->i_gid = current_fsgid();
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index e4f595055208..f276e9594f00 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -980,6 +980,7 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
if (likely(inode)) {
struct timespec current_time = CURRENT_TIME;
+ inode->i_ino = get_next_ino();
inode->i_mode = perms->mode;
inode->i_uid = perms->uid;
inode->i_gid = perms->gid;
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index d1d72d946b04..ba145e7fbe03 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -1991,6 +1991,7 @@ gadgetfs_make_inode (struct super_block *sb,
struct inode *inode = new_inode (sb);
if (inode) {
+ inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_uid = default_uid;
inode->i_gid = default_gid;
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 61d3ca6619bb..cb5cd422f3f5 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -54,7 +54,6 @@
#include <plat/dma.h>
#include <plat/usb.h>
-#include <plat/control.h>
#include "omap_udc.h"
@@ -2309,21 +2308,12 @@ static char *trx_mode(unsigned m, int enabled)
static int proc_otg_show(struct seq_file *s)
{
u32 tmp;
- u32 trans;
- char *ctrl_name;
+ u32 trans = 0;
+ char *ctrl_name = "(UNKNOWN)";
+ /* XXX This needs major revision for OMAP2+ */
tmp = omap_readl(OTG_REV);
- if (cpu_is_omap24xx()) {
- /*
- * REVISIT: Not clear how this works on OMAP2. trans
- * is ANDed to produce bits 7 and 8, which might make
- * sense for USB_TRANSCEIVER_CTRL on OMAP1,
- * but with CONTROL_DEVCONF, these bits have something to
- * do with the frame adjustment counter and McBSP2.
- */
- ctrl_name = "control_devconf";
- trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
- } else {
+ if (cpu_class_is_omap1()) {
ctrl_name = "tranceiver_ctrl";
trans = omap_readw(USB_TRANSCEIVER_CTRL);
}
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 77be3c24a427..3076b1cc05df 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2397,7 +2397,7 @@ static const struct dev_pm_ops r8a66597_dev_pm_ops = {
#define R8A66597_DEV_PM_OPS NULL
#endif
-static int __init_or_module r8a66597_remove(struct platform_device *pdev)
+static int __devexit r8a66597_remove(struct platform_device *pdev)
{
struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
@@ -2542,7 +2542,7 @@ clean_up:
static struct platform_driver r8a66597_driver = {
.probe = r8a66597_probe,
- .remove = r8a66597_remove,
+ .remove = __devexit_p(r8a66597_remove),
.driver = {
.name = (char *) hcd_name,
.owner = THIS_MODULE,
diff --git a/drivers/uwb/Kconfig b/drivers/uwb/Kconfig
index bac8e7a6f17b..d100f54ed650 100644
--- a/drivers/uwb/Kconfig
+++ b/drivers/uwb/Kconfig
@@ -12,8 +12,7 @@ menuconfig UWB
technology using a wide spectrum (3.1-10.6GHz). It is
optimized for in-room use (480Mbps at 2 meters, 110Mbps at
10m). It serves as the transport layer for other protocols,
- such as Wireless USB (WUSB), IP (WLP) and upcoming
- Bluetooth and 1394
+ such as Wireless USB (WUSB).
The topology is peer to peer; however, higher level
protocols (such as WUSB) might impose a master/slave
@@ -58,13 +57,6 @@ config UWB_WHCI
To compile this driver select Y (built in) or M (module). It
is safe to select any even if you do not have the hardware.
-config UWB_WLP
- tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)"
- depends on UWB && NET
- help
- This is a common library for drivers that implement
- networking over UWB.
-
config UWB_I1480U
tristate "Support for Intel Wireless UWB Link 1480 HWA"
depends on UWB_HWA
@@ -77,14 +69,4 @@ config UWB_I1480U
To compile this driver select Y (built in) or M (module). It
is safe to select any even if you do not have the hardware.
-config UWB_I1480U_WLP
- tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface"
- depends on UWB_I1480U && UWB_WLP && NET
- help
- This driver enables WLP support for the i1480 when connected via
- USB. WLP is the WiMedia Link Protocol, or IP over UWB.
-
- To compile this driver select Y (built in) or M (module). It
- is safe to select any even if you don't have the hardware.
-
endif # UWB
diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile
index 2f98d080fe78..d47dd6e2942c 100644
--- a/drivers/uwb/Makefile
+++ b/drivers/uwb/Makefile
@@ -1,5 +1,4 @@
obj-$(CONFIG_UWB) += uwb.o
-obj-$(CONFIG_UWB_WLP) += wlp/
obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o
obj-$(CONFIG_UWB_HWA) += hwa-rc.o
obj-$(CONFIG_UWB_I1480U) += i1480/
diff --git a/drivers/uwb/i1480/Makefile b/drivers/uwb/i1480/Makefile
index 212bbc7d4c32..d69da1684cfb 100644
--- a/drivers/uwb/i1480/Makefile
+++ b/drivers/uwb/i1480/Makefile
@@ -1,2 +1 @@
obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
-obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/
diff --git a/drivers/uwb/i1480/i1480-wlp.h b/drivers/uwb/i1480/i1480-wlp.h
deleted file mode 100644
index 18a8b0e4567b..000000000000
--- a/drivers/uwb/i1480/i1480-wlp.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Intel 1480 Wireless UWB Link
- * WLP specific definitions
- *
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: docs
- */
-
-#ifndef __i1480_wlp_h__
-#define __i1480_wlp_h__
-
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/uwb.h>
-#include <linux/if_ether.h>
-#include <asm/byteorder.h>
-
-/* New simplified header format? */
-#undef WLP_HDR_FMT_2 /* FIXME: rename */
-
-/**
- * Values of the Delivery ID & Type field when PCA or DRP
- *
- * The Delivery ID & Type field in the WLP TX header indicates whether
- * the frame is PCA or DRP. This is done based on the high level bit of
- * this field.
- * We use this constant to test if the traffic is PCA or DRP as follows:
- * if (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)
- * this is DRP traffic
- * else
- * this is PCA traffic
- */
-enum deliver_id_type_bit {
- WLP_DRP = 8,
-};
-
-/**
- * WLP TX header
- *
- * Indicates UWB/WLP-specific transmission parameters for a network
- * packet.
- */
-struct wlp_tx_hdr {
- /* dword 0 */
- struct uwb_dev_addr dstaddr;
- u8 key_index;
- u8 mac_params;
- /* dword 1 */
- u8 phy_params;
-#ifndef WLP_HDR_FMT_2
- u8 reserved;
- __le16 oui01; /* FIXME: not so sure if __le16 or u8[2] */
- /* dword 2 */
- u8 oui2; /* if all LE, it could be merged */
- __le16 prid;
-#endif
-} __attribute__((packed));
-
-static inline int wlp_tx_hdr_delivery_id_type(const struct wlp_tx_hdr *hdr)
-{
- return hdr->mac_params & 0x0f;
-}
-
-static inline int wlp_tx_hdr_ack_policy(const struct wlp_tx_hdr *hdr)
-{
- return (hdr->mac_params >> 4) & 0x07;
-}
-
-static inline int wlp_tx_hdr_rts_cts(const struct wlp_tx_hdr *hdr)
-{
- return (hdr->mac_params >> 7) & 0x01;
-}
-
-static inline void wlp_tx_hdr_set_delivery_id_type(struct wlp_tx_hdr *hdr, int id)
-{
- hdr->mac_params = (hdr->mac_params & ~0x0f) | id;
-}
-
-static inline void wlp_tx_hdr_set_ack_policy(struct wlp_tx_hdr *hdr,
- enum uwb_ack_pol policy)
-{
- hdr->mac_params = (hdr->mac_params & ~0x70) | (policy << 4);
-}
-
-static inline void wlp_tx_hdr_set_rts_cts(struct wlp_tx_hdr *hdr, int rts_cts)
-{
- hdr->mac_params = (hdr->mac_params & ~0x80) | (rts_cts << 7);
-}
-
-static inline enum uwb_phy_rate wlp_tx_hdr_phy_rate(const struct wlp_tx_hdr *hdr)
-{
- return hdr->phy_params & 0x0f;
-}
-
-static inline int wlp_tx_hdr_tx_power(const struct wlp_tx_hdr *hdr)
-{
- return (hdr->phy_params >> 4) & 0x0f;
-}
-
-static inline void wlp_tx_hdr_set_phy_rate(struct wlp_tx_hdr *hdr, enum uwb_phy_rate rate)
-{
- hdr->phy_params = (hdr->phy_params & ~0x0f) | rate;
-}
-
-static inline void wlp_tx_hdr_set_tx_power(struct wlp_tx_hdr *hdr, int pwr)
-{
- hdr->phy_params = (hdr->phy_params & ~0xf0) | (pwr << 4);
-}
-
-
-/**
- * WLP RX header
- *
- * Provides UWB/WLP-specific transmission data for a received
- * network packet.
- */
-struct wlp_rx_hdr {
- /* dword 0 */
- struct uwb_dev_addr dstaddr;
- struct uwb_dev_addr srcaddr;
- /* dword 1 */
- u8 LQI;
- s8 RSSI;
- u8 reserved3;
-#ifndef WLP_HDR_FMT_2
- u8 oui0;
- /* dword 2 */
- __le16 oui12;
- __le16 prid;
-#endif
-} __attribute__((packed));
-
-
-/** User configurable options for WLP */
-struct wlp_options {
- struct mutex mutex; /* access to user configurable options*/
- struct wlp_tx_hdr def_tx_hdr; /* default tx hdr */
- u8 pca_base_priority;
- u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/
-};
-
-
-static inline
-void wlp_options_init(struct wlp_options *options)
-{
- mutex_init(&options->mutex);
- wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, UWB_ACK_INM);
- wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, 1);
- /* FIXME: default to phy caps */
- wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, UWB_PHY_RATE_480);
-#ifndef WLP_HDR_FMT_2
- options->def_tx_hdr.prid = cpu_to_le16(0x0000);
-#endif
-}
-
-
-/* sysfs helpers */
-
-extern ssize_t uwb_pca_base_priority_store(struct wlp_options *,
- const char *, size_t);
-extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *);
-extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t);
-extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *);
-extern ssize_t uwb_ack_policy_store(struct wlp_options *,
- const char *, size_t);
-extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *);
-extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t);
-extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *);
-extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t);
-extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *);
-
-
-/** Simple bandwidth allocation (temporary and too simple) */
-struct wlp_bw_allocs {
- const char *name;
- struct {
- u8 mask, stream;
- } tx, rx;
-};
-
-
-#endif /* #ifndef __i1480_wlp_h__ */
diff --git a/drivers/uwb/i1480/i1480u-wlp/Makefile b/drivers/uwb/i1480/i1480u-wlp/Makefile
deleted file mode 100644
index fe6709b8e68b..000000000000
--- a/drivers/uwb/i1480/i1480u-wlp/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o
-
-i1480u-wlp-objs := \
- lc.o \
- netdev.o \
- rx.o \
- sysfs.o \
- tx.o
diff --git a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h
deleted file mode 100644
index 2e31f536a347..000000000000
--- a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Intel 1480 Wireless UWB Link USB
- * Header formats, constants, general internal interfaces
- *
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * This is not an standard interface.
- *
- * FIXME: docs
- *
- * i1480u-wlp is pretty simple: two endpoints, one for tx, one for
- * rx. rx is polled. Network packets (ethernet, whatever) are wrapped
- * in i1480 TX or RX headers (for sending over the air), and these
- * packets are wrapped in UNTD headers (for sending to the WLP UWB
- * controller).
- *
- * UNTD packets (UNTD hdr + i1480 hdr + network packet) packets
- * cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the
- * i1480 packet is broken in chunks/packets:
- *
- * UNTD-1st.hdr + i1480.hdr + payload
- * UNTD-next.hdr + payload
- * ...
- * UNTD-last.hdr + payload
- *
- * so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE.
- *
- * All HW structures and bitmaps are little endian, so we need to play
- * ugly tricks when defining bitfields. Hoping for the day GCC
- * implements __attribute__((endian(1234))).
- *
- * FIXME: ROADMAP to the whole implementation
- */
-
-#ifndef __i1480u_wlp_h__
-#define __i1480u_wlp_h__
-
-#include <linux/usb.h>
-#include <linux/netdevice.h>
-#include <linux/uwb.h> /* struct uwb_rc, struct uwb_notifs_handler */
-#include <linux/wlp.h>
-#include "../i1480-wlp.h"
-
-#undef i1480u_FLOW_CONTROL /* Enable flow control code */
-
-/**
- * Basic flow control
- */
-enum {
- i1480u_TX_INFLIGHT_MAX = 1000,
- i1480u_TX_INFLIGHT_THRESHOLD = 100,
-};
-
-/** Maximum size of a transaction that we can tx/rx */
-enum {
- /* Maximum packet size computed as follows: max UNTD header (8) +
- * i1480 RX header (8) + max Ethernet header and payload (4096) +
- * Padding added by skb_reserve (2) to make post Ethernet payload
- * start on 16 byte boundary*/
- i1480u_MAX_RX_PKT_SIZE = 4114,
- i1480u_MAX_FRG_SIZE = 512,
- i1480u_RX_BUFS = 9,
-};
-
-
-/**
- * UNTD packet type
- *
- * We need to fragment any payload whose UNTD packet is going to be
- * bigger than i1480u_MAX_FRG_SIZE.
- */
-enum i1480u_pkt_type {
- i1480u_PKT_FRAG_1ST = 0x1,
- i1480u_PKT_FRAG_NXT = 0x0,
- i1480u_PKT_FRAG_LST = 0x2,
- i1480u_PKT_FRAG_CMP = 0x3
-};
-enum {
- i1480u_PKT_NONE = 0x4,
-};
-
-/** USB Network Transfer Descriptor - common */
-struct untd_hdr {
- u8 type;
- __le16 len;
-} __attribute__((packed));
-
-static inline enum i1480u_pkt_type untd_hdr_type(const struct untd_hdr *hdr)
-{
- return hdr->type & 0x03;
-}
-
-static inline int untd_hdr_rx_tx(const struct untd_hdr *hdr)
-{
- return (hdr->type >> 2) & 0x01;
-}
-
-static inline void untd_hdr_set_type(struct untd_hdr *hdr, enum i1480u_pkt_type type)
-{
- hdr->type = (hdr->type & ~0x03) | type;
-}
-
-static inline void untd_hdr_set_rx_tx(struct untd_hdr *hdr, int rx_tx)
-{
- hdr->type = (hdr->type & ~0x04) | (rx_tx << 2);
-}
-
-
-/**
- * USB Network Transfer Descriptor - Complete Packet
- *
- * This is for a packet that is smaller (header + payload) than
- * i1480u_MAX_FRG_SIZE.
- *
- * @hdr.total_len is the size of the payload; the payload doesn't
- * count this header nor the padding, but includes the size of i1480
- * header.
- */
-struct untd_hdr_cmp {
- struct untd_hdr hdr;
- u8 padding;
-} __attribute__((packed));
-
-
-/**
- * USB Network Transfer Descriptor - First fragment
- *
- * @hdr.len is the size of the *whole packet* (excluding UNTD
- * headers); @fragment_len is the size of the payload (excluding UNTD
- * headers, but including i1480 headers).
- */
-struct untd_hdr_1st {
- struct untd_hdr hdr;
- __le16 fragment_len;
- u8 padding[3];
-} __attribute__((packed));
-
-
-/**
- * USB Network Transfer Descriptor - Next / Last [Rest]
- *
- * @hdr.len is the size of the payload, not including headrs.
- */
-struct untd_hdr_rst {
- struct untd_hdr hdr;
- u8 padding;
-} __attribute__((packed));
-
-
-/**
- * Transmission context
- *
- * Wraps all the stuff needed to track a pending/active tx
- * operation.
- */
-struct i1480u_tx {
- struct list_head list_node;
- struct i1480u *i1480u;
- struct urb *urb;
-
- struct sk_buff *skb;
- struct wlp_tx_hdr *wlp_tx_hdr;
-
- void *buf; /* if NULL, no new buf was used */
- size_t buf_size;
-};
-
-/**
- * Basic flow control
- *
- * We maintain a basic flow control counter. "count" how many TX URBs are
- * outstanding. Only allow "max"
- * TX URBs to be outstanding. If this value is reached the queue will be
- * stopped. The queue will be restarted when there are
- * "threshold" URBs outstanding.
- * Maintain a counter of how many time the TX queue needed to be restarted
- * due to the "max" being exceeded and the "threshold" reached again. The
- * timestamp "restart_ts" is to keep track from when the counter was last
- * queried (see sysfs handling of file wlp_tx_inflight).
- */
-struct i1480u_tx_inflight {
- atomic_t count;
- unsigned long max;
- unsigned long threshold;
- unsigned long restart_ts;
- atomic_t restart_count;
-};
-
-/**
- * Instance of a i1480u WLP interface
- *
- * Keeps references to the USB device that wraps it, as well as it's
- * interface and associated UWB host controller. As well, it also
- * keeps a link to the netdevice for integration into the networking
- * stack.
- * We maintian separate error history for the tx and rx endpoints because
- * the implementation does not rely on locking - having one shared
- * structure between endpoints may cause problems. Adding locking to the
- * implementation will have higher cost than adding a separate structure.
- */
-struct i1480u {
- struct usb_device *usb_dev;
- struct usb_interface *usb_iface;
- struct net_device *net_dev;
-
- spinlock_t lock;
-
- /* RX context handling */
- struct sk_buff *rx_skb;
- struct uwb_dev_addr rx_srcaddr;
- size_t rx_untd_pkt_size;
- struct i1480u_rx_buf {
- struct i1480u *i1480u; /* back pointer */
- struct urb *urb;
- struct sk_buff *data; /* i1480u_MAX_RX_PKT_SIZE each */
- } rx_buf[i1480u_RX_BUFS]; /* N bufs */
-
- spinlock_t tx_list_lock; /* TX context */
- struct list_head tx_list;
- u8 tx_stream;
-
- struct stats lqe_stats, rssi_stats; /* radio statistics */
-
- /* Options we can set from sysfs */
- struct wlp_options options;
- struct uwb_notifs_handler uwb_notifs_handler;
- struct edc tx_errors;
- struct edc rx_errors;
- struct wlp wlp;
-#ifdef i1480u_FLOW_CONTROL
- struct urb *notif_urb;
- struct edc notif_edc; /* error density counter */
- u8 notif_buffer[1];
-#endif
- struct i1480u_tx_inflight tx_inflight;
-};
-
-/* Internal interfaces */
-extern void i1480u_rx_cb(struct urb *urb);
-extern int i1480u_rx_setup(struct i1480u *);
-extern void i1480u_rx_release(struct i1480u *);
-extern void i1480u_tx_release(struct i1480u *);
-extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *,
- struct uwb_dev_addr *);
-extern void i1480u_stop_queue(struct wlp *);
-extern void i1480u_start_queue(struct wlp *);
-extern int i1480u_sysfs_setup(struct i1480u *);
-extern void i1480u_sysfs_release(struct i1480u *);
-
-/* netdev interface */
-extern int i1480u_open(struct net_device *);
-extern int i1480u_stop(struct net_device *);
-extern netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *,
- struct net_device *);
-extern void i1480u_tx_timeout(struct net_device *);
-extern int i1480u_set_config(struct net_device *, struct ifmap *);
-extern int i1480u_change_mtu(struct net_device *, int);
-extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs);
-
-/* bandwidth allocation callback */
-extern void i1480u_bw_alloc_cb(struct uwb_rsv *);
-
-/* Sys FS */
-extern struct attribute_group i1480u_wlp_attr_group;
-
-#endif /* #ifndef __i1480u_wlp_h__ */
diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c
deleted file mode 100644
index def778cf2216..000000000000
--- a/drivers/uwb/i1480/i1480u-wlp/lc.c
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * WUSB Wire Adapter: WLP interface
- * Driver for the Linux Network stack.
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: docs
- *
- * This implements a very simple network driver for the WLP USB
- * device that is associated to a UWB (Ultra Wide Band) host.
- *
- * This is seen as an interface of a composite device. Once the UWB
- * host has an association to another WLP capable device, the
- * networking interface (aka WLP) can start to send packets back and
- * forth.
- *
- * Limitations:
- *
- * - Hand cranked; can't ifup the interface until there is an association
- *
- * - BW allocation very simplistic [see i1480u_mas_set() and callees].
- *
- *
- * ROADMAP:
- *
- * ENTRY POINTS (driver model):
- *
- * i1480u_driver_{exit,init}(): initialization of the driver.
- *
- * i1480u_probe(): called by the driver code when a device
- * matching 'i1480u_id_table' is connected.
- *
- * This allocs a netdev instance, inits with
- * i1480u_add(), then registers_netdev().
- * i1480u_init()
- * i1480u_add()
- *
- * i1480u_disconnect(): device has been disconnected/module
- * is being removed.
- * i1480u_rm()
- */
-#include <linux/gfp.h>
-#include <linux/if_arp.h>
-#include <linux/etherdevice.h>
-
-#include "i1480u-wlp.h"
-
-
-
-static inline
-void i1480u_init(struct i1480u *i1480u)
-{
- /* nothing so far... doesn't it suck? */
- spin_lock_init(&i1480u->lock);
- INIT_LIST_HEAD(&i1480u->tx_list);
- spin_lock_init(&i1480u->tx_list_lock);
- wlp_options_init(&i1480u->options);
- edc_init(&i1480u->tx_errors);
- edc_init(&i1480u->rx_errors);
-#ifdef i1480u_FLOW_CONTROL
- edc_init(&i1480u->notif_edc);
-#endif
- stats_init(&i1480u->lqe_stats);
- stats_init(&i1480u->rssi_stats);
- wlp_init(&i1480u->wlp);
-}
-
-/**
- * Fill WLP device information structure
- *
- * The structure will contain a few character arrays, each ending with a
- * null terminated string. Each string has to fit (excluding terminating
- * character) into a specified range obtained from the WLP substack.
- *
- * It is still not clear exactly how this device information should be
- * obtained. Until we find out we use the USB device descriptor as backup, some
- * information elements have intuitive mappings, other not.
- */
-static
-void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info)
-{
- struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
- struct usb_device *usb_dev = i1480u->usb_dev;
- /* Treat device name and model name the same */
- if (usb_dev->descriptor.iProduct) {
- usb_string(usb_dev, usb_dev->descriptor.iProduct,
- dev_info->name, sizeof(dev_info->name));
- usb_string(usb_dev, usb_dev->descriptor.iProduct,
- dev_info->model_name, sizeof(dev_info->model_name));
- }
- if (usb_dev->descriptor.iManufacturer)
- usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
- dev_info->manufacturer,
- sizeof(dev_info->manufacturer));
- scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x",
- __le16_to_cpu(usb_dev->descriptor.bcdDevice));
- if (usb_dev->descriptor.iSerialNumber)
- usb_string(usb_dev, usb_dev->descriptor.iSerialNumber,
- dev_info->serial, sizeof(dev_info->serial));
- /* FIXME: where should we obtain category? */
- dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER);
- /* FIXME: Complete OUI and OUIsubdiv attributes */
-}
-
-#ifdef i1480u_FLOW_CONTROL
-/**
- * Callback for the notification endpoint
- *
- * This mostly controls the xon/xoff protocol. In case of hard error,
- * we stop the queue. If not, we always retry.
- */
-static
-void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs)
-{
- struct i1480u *i1480u = urb->context;
- struct usb_interface *usb_iface = i1480u->usb_iface;
- struct device *dev = &usb_iface->dev;
- int result;
-
- switch (urb->status) {
- case 0: /* Got valid data, do xon/xoff */
- switch (i1480u->notif_buffer[0]) {
- case 'N':
- dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies);
- netif_stop_queue(i1480u->net_dev);
- break;
- case 'A':
- dev_err(dev, "XON STARTING queue at %lu\n", jiffies);
- netif_start_queue(i1480u->net_dev);
- break;
- default:
- dev_err(dev, "NEP: unknown data 0x%02hhx\n",
- i1480u->notif_buffer[0]);
- }
- break;
- case -ECONNRESET: /* Controlled situation ... */
- case -ENOENT: /* we killed the URB... */
- dev_err(dev, "NEP: URB reset/noent %d\n", urb->status);
- goto error;
- case -ESHUTDOWN: /* going away! */
- dev_err(dev, "NEP: URB down %d\n", urb->status);
- goto error;
- default: /* Retry unless it gets ugly */
- if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS,
- EDC_ERROR_TIMEFRAME)) {
- dev_err(dev, "NEP: URB max acceptable errors "
- "exceeded; resetting device\n");
- goto error_reset;
- }
- dev_err(dev, "NEP: URB error %d\n", urb->status);
- break;
- }
- result = usb_submit_urb(urb, GFP_ATOMIC);
- if (result < 0) {
- dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n",
- result);
- goto error_reset;
- }
- return;
-
-error_reset:
- wlp_reset_all(&i1480-wlp);
-error:
- netif_stop_queue(i1480u->net_dev);
- return;
-}
-#endif
-
-static const struct net_device_ops i1480u_netdev_ops = {
- .ndo_open = i1480u_open,
- .ndo_stop = i1480u_stop,
- .ndo_start_xmit = i1480u_hard_start_xmit,
- .ndo_tx_timeout = i1480u_tx_timeout,
- .ndo_set_config = i1480u_set_config,
- .ndo_change_mtu = i1480u_change_mtu,
-};
-
-static
-int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface)
-{
- int result = -ENODEV;
- struct wlp *wlp = &i1480u->wlp;
- struct usb_device *usb_dev = interface_to_usbdev(iface);
- struct net_device *net_dev = i1480u->net_dev;
- struct uwb_rc *rc;
- struct uwb_dev *uwb_dev;
-#ifdef i1480u_FLOW_CONTROL
- struct usb_endpoint_descriptor *epd;
-#endif
-
- i1480u->usb_dev = usb_get_dev(usb_dev);
- i1480u->usb_iface = iface;
- rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev);
- if (rc == NULL) {
- dev_err(&iface->dev, "Cannot get associated UWB Radio "
- "Controller\n");
- goto out;
- }
- wlp->xmit_frame = i1480u_xmit_frame;
- wlp->fill_device_info = i1480u_fill_device_info;
- wlp->stop_queue = i1480u_stop_queue;
- wlp->start_queue = i1480u_start_queue;
- result = wlp_setup(wlp, rc, net_dev);
- if (result < 0) {
- dev_err(&iface->dev, "Cannot setup WLP\n");
- goto error_wlp_setup;
- }
- result = 0;
- ether_setup(net_dev); /* make it an etherdevice */
- uwb_dev = &rc->uwb_dev;
- /* FIXME: hookup address change notifications? */
-
- memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data,
- sizeof(net_dev->dev_addr));
-
- net_dev->hard_header_len = sizeof(struct untd_hdr_cmp)
- + sizeof(struct wlp_tx_hdr)
- + WLP_DATA_HLEN
- + ETH_HLEN;
- net_dev->mtu = 3500;
- net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */
-
-/* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */
- /* FIXME: multicast disabled */
- net_dev->flags &= ~IFF_MULTICAST;
- net_dev->features &= ~NETIF_F_SG;
- net_dev->features &= ~NETIF_F_FRAGLIST;
- /* All NETIF_F_*_CSUM disabled */
- net_dev->features |= NETIF_F_HIGHDMA;
- net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */
-
- net_dev->netdev_ops = &i1480u_netdev_ops;
-
-#ifdef i1480u_FLOW_CONTROL
- /* Notification endpoint setup (submitted when we open the device) */
- i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (i1480u->notif_urb == NULL) {
- dev_err(&iface->dev, "Unable to allocate notification URB\n");
- result = -ENOMEM;
- goto error_urb_alloc;
- }
- epd = &iface->cur_altsetting->endpoint[0].desc;
- usb_fill_int_urb(i1480u->notif_urb, usb_dev,
- usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
- i1480u->notif_buffer, sizeof(i1480u->notif_buffer),
- i1480u_notif_cb, i1480u, epd->bInterval);
-
-#endif
-
- i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX;
- i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
- i1480u->tx_inflight.restart_ts = jiffies;
- usb_set_intfdata(iface, i1480u);
- return result;
-
-#ifdef i1480u_FLOW_CONTROL
-error_urb_alloc:
-#endif
- wlp_remove(wlp);
-error_wlp_setup:
- uwb_rc_put(rc);
-out:
- usb_put_dev(i1480u->usb_dev);
- return result;
-}
-
-static void i1480u_rm(struct i1480u *i1480u)
-{
- struct uwb_rc *rc = i1480u->wlp.rc;
- usb_set_intfdata(i1480u->usb_iface, NULL);
-#ifdef i1480u_FLOW_CONTROL
- usb_kill_urb(i1480u->notif_urb);
- usb_free_urb(i1480u->notif_urb);
-#endif
- wlp_remove(&i1480u->wlp);
- uwb_rc_put(rc);
- usb_put_dev(i1480u->usb_dev);
-}
-
-/** Just setup @net_dev's i1480u private data */
-static void i1480u_netdev_setup(struct net_device *net_dev)
-{
- struct i1480u *i1480u = netdev_priv(net_dev);
- /* Initialize @i1480u */
- memset(i1480u, 0, sizeof(*i1480u));
- i1480u_init(i1480u);
-}
-
-/**
- * Probe a i1480u interface and register it
- *
- * @iface: USB interface to link to
- * @id: USB class/subclass/protocol id
- * @returns: 0 if ok, < 0 errno code on error.
- *
- * Does basic housekeeping stuff and then allocs a netdev with space
- * for the i1480u data. Initializes, registers in i1480u, registers in
- * netdev, ready to go.
- */
-static int i1480u_probe(struct usb_interface *iface,
- const struct usb_device_id *id)
-{
- int result;
- struct net_device *net_dev;
- struct device *dev = &iface->dev;
- struct i1480u *i1480u;
-
- /* Allocate instance [calls i1480u_netdev_setup() on it] */
- result = -ENOMEM;
- net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup);
- if (net_dev == NULL) {
- dev_err(dev, "no memory for network device instance\n");
- goto error_alloc_netdev;
- }
- SET_NETDEV_DEV(net_dev, dev);
- i1480u = netdev_priv(net_dev);
- i1480u->net_dev = net_dev;
- result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */
- if (result < 0) {
- dev_err(dev, "cannot add i1480u device: %d\n", result);
- goto error_i1480u_add;
- }
- result = register_netdev(net_dev); /* Okey dokey, bring it up */
- if (result < 0) {
- dev_err(dev, "cannot register network device: %d\n", result);
- goto error_register_netdev;
- }
- i1480u_sysfs_setup(i1480u);
- if (result < 0)
- goto error_sysfs_init;
- return 0;
-
-error_sysfs_init:
- unregister_netdev(net_dev);
-error_register_netdev:
- i1480u_rm(i1480u);
-error_i1480u_add:
- free_netdev(net_dev);
-error_alloc_netdev:
- return result;
-}
-
-
-/**
- * Disconect a i1480u from the system.
- *
- * i1480u_stop() has been called before, so al the rx and tx contexts
- * have been taken down already. Make sure the queue is stopped,
- * unregister netdev and i1480u, free and kill.
- */
-static void i1480u_disconnect(struct usb_interface *iface)
-{
- struct i1480u *i1480u;
- struct net_device *net_dev;
-
- i1480u = usb_get_intfdata(iface);
- net_dev = i1480u->net_dev;
- netif_stop_queue(net_dev);
-#ifdef i1480u_FLOW_CONTROL
- usb_kill_urb(i1480u->notif_urb);
-#endif
- i1480u_sysfs_release(i1480u);
- unregister_netdev(net_dev);
- i1480u_rm(i1480u);
- free_netdev(net_dev);
-}
-
-static struct usb_device_id i1480u_id_table[] = {
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE \
- | USB_DEVICE_ID_MATCH_DEV_INFO \
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x8086,
- .idProduct = 0x0c3b,
- .bDeviceClass = 0xef,
- .bDeviceSubClass = 0x02,
- .bDeviceProtocol = 0x02,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- },
- {},
-};
-MODULE_DEVICE_TABLE(usb, i1480u_id_table);
-
-static struct usb_driver i1480u_driver = {
- .name = KBUILD_MODNAME,
- .probe = i1480u_probe,
- .disconnect = i1480u_disconnect,
- .id_table = i1480u_id_table,
-};
-
-static int __init i1480u_driver_init(void)
-{
- return usb_register(&i1480u_driver);
-}
-module_init(i1480u_driver_init);
-
-
-static void __exit i1480u_driver_exit(void)
-{
- usb_deregister(&i1480u_driver);
-}
-module_exit(i1480u_driver_exit);
-
-MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
-MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB");
-MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c
deleted file mode 100644
index f98f6ce8b9e7..000000000000
--- a/drivers/uwb/i1480/i1480u-wlp/netdev.c
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * WUSB Wire Adapter: WLP interface
- * Driver for the Linux Network stack.
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: docs
- *
- * Implementation of the netdevice linkage (except tx and rx related stuff).
- *
- * ROADMAP:
- *
- * ENTRY POINTS (Net device):
- *
- * i1480u_open(): Called when we ifconfig up the interface;
- * associates to a UWB host controller, reserves
- * bandwidth (MAS), sets up RX USB URB and starts
- * the queue.
- *
- * i1480u_stop(): Called when we ifconfig down a interface;
- * reverses _open().
- *
- * i1480u_set_config():
- */
-
-#include <linux/slab.h>
-#include <linux/if_arp.h>
-#include <linux/etherdevice.h>
-
-#include "i1480u-wlp.h"
-
-struct i1480u_cmd_set_ip_mas {
- struct uwb_rccb rccb;
- struct uwb_dev_addr addr;
- u8 stream;
- u8 owner;
- u8 type; /* enum uwb_drp_type */
- u8 baMAS[32];
-} __attribute__((packed));
-
-
-static
-int i1480u_set_ip_mas(
- struct uwb_rc *rc,
- const struct uwb_dev_addr *dstaddr,
- u8 stream, u8 owner, u8 type, unsigned long *mas)
-{
-
- int result;
- struct i1480u_cmd_set_ip_mas *cmd;
- struct uwb_rc_evt_confirm reply;
-
- result = -ENOMEM;
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
- if (cmd == NULL)
- goto error_kzalloc;
- cmd->rccb.bCommandType = 0xfd;
- cmd->rccb.wCommand = cpu_to_le16(0x000e);
- cmd->addr = *dstaddr;
- cmd->stream = stream;
- cmd->owner = owner;
- cmd->type = type;
- if (mas == NULL)
- memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS));
- else
- memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS));
- reply.rceb.bEventType = 0xfd;
- reply.rceb.wEvent = cpu_to_le16(0x000e);
- result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd),
- &reply.rceb, sizeof(reply));
- if (result < 0)
- goto error_cmd;
- if (reply.bResultCode != UWB_RC_RES_FAIL) {
- dev_err(&rc->uwb_dev.dev,
- "SET-IP-MAS: command execution failed: %d\n",
- reply.bResultCode);
- result = -EIO;
- }
-error_cmd:
- kfree(cmd);
-error_kzalloc:
- return result;
-}
-
-/*
- * Inform a WLP interface of a MAS reservation
- *
- * @rc is assumed refcnted.
- */
-/* FIXME: detect if remote device is WLP capable? */
-static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc,
- u8 stream, u8 owner, u8 type, unsigned long *mas)
-{
- int result = 0;
- struct device *dev = &rc->uwb_dev.dev;
-
- result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner,
- type, mas);
- if (result < 0) {
- char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE];
- uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf),
- &rc->uwb_dev.dev_addr);
- uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf),
- &uwb_dev->dev_addr);
- dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n",
- rcaddrbuf, devaddrbuf, result);
- }
- return result;
-}
-
-/**
- * Called by bandwidth allocator when change occurs in reservation.
- *
- * @rsv: The reservation that is being established, modified, or
- * terminated.
- *
- * When a reservation is established, modified, or terminated the upper layer
- * (WLP here) needs set/update the currently available Media Access Slots
- * that can be use for IP traffic.
- *
- * Our action taken during failure depends on how the reservation is being
- * changed:
- * - if reservation is being established we do nothing if we cannot set the
- * new MAS to be used
- * - if reservation is being terminated we revert back to PCA whether the
- * SET IP MAS command succeeds or not.
- */
-void i1480u_bw_alloc_cb(struct uwb_rsv *rsv)
-{
- int result = 0;
- struct i1480u *i1480u = rsv->pal_priv;
- struct device *dev = &i1480u->usb_iface->dev;
- struct uwb_dev *target_dev = rsv->target.dev;
- struct uwb_rc *rc = i1480u->wlp.rc;
- u8 stream = rsv->stream;
- int type = rsv->type;
- int is_owner = rsv->owner == &rc->uwb_dev;
- unsigned long *bmp = rsv->mas.bm;
-
- dev_err(dev, "WLP callback called - sending set ip mas\n");
- /*user cannot change options while setting configuration*/
- mutex_lock(&i1480u->options.mutex);
- switch (rsv->state) {
- case UWB_RSV_STATE_T_ACCEPTED:
- case UWB_RSV_STATE_O_ESTABLISHED:
- result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
- type, bmp);
- if (result < 0) {
- dev_err(dev, "MAS reservation failed: %d\n", result);
- goto out;
- }
- if (is_owner) {
- wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
- WLP_DRP | stream);
- wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0);
- }
- break;
- case UWB_RSV_STATE_NONE:
- /* revert back to PCA */
- result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
- type, bmp);
- if (result < 0)
- dev_err(dev, "MAS reservation failed: %d\n", result);
- /* Revert to PCA even though SET IP MAS failed. */
- wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
- i1480u->options.pca_base_priority);
- wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1);
- break;
- default:
- dev_err(dev, "unexpected WLP reservation state: %s (%d).\n",
- uwb_rsv_state_str(rsv->state), rsv->state);
- break;
- }
-out:
- mutex_unlock(&i1480u->options.mutex);
- return;
-}
-
-/**
- *
- * Called on 'ifconfig up'
- */
-int i1480u_open(struct net_device *net_dev)
-{
- int result;
- struct i1480u *i1480u = netdev_priv(net_dev);
- struct wlp *wlp = &i1480u->wlp;
- struct uwb_rc *rc;
- struct device *dev = &i1480u->usb_iface->dev;
-
- rc = wlp->rc;
- result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */
- if (result < 0)
- goto error_rx_setup;
-
- result = uwb_radio_start(&wlp->pal);
- if (result < 0)
- goto error_radio_start;
-
- netif_wake_queue(net_dev);
-#ifdef i1480u_FLOW_CONTROL
- result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);
- if (result < 0) {
- dev_err(dev, "Can't submit notification URB: %d\n", result);
- goto error_notif_urb_submit;
- }
-#endif
- /* Interface is up with an address, now we can create WSS */
- result = wlp_wss_setup(net_dev, &wlp->wss);
- if (result < 0) {
- dev_err(dev, "Can't create WSS: %d. \n", result);
- goto error_wss_setup;
- }
- return 0;
-error_wss_setup:
-#ifdef i1480u_FLOW_CONTROL
- usb_kill_urb(i1480u->notif_urb);
-error_notif_urb_submit:
-#endif
- uwb_radio_stop(&wlp->pal);
-error_radio_start:
- netif_stop_queue(net_dev);
- i1480u_rx_release(i1480u);
-error_rx_setup:
- return result;
-}
-
-
-/**
- * Called on 'ifconfig down'
- */
-int i1480u_stop(struct net_device *net_dev)
-{
- struct i1480u *i1480u = netdev_priv(net_dev);
- struct wlp *wlp = &i1480u->wlp;
-
- BUG_ON(wlp->rc == NULL);
- wlp_wss_remove(&wlp->wss);
- netif_carrier_off(net_dev);
-#ifdef i1480u_FLOW_CONTROL
- usb_kill_urb(i1480u->notif_urb);
-#endif
- netif_stop_queue(net_dev);
- uwb_radio_stop(&wlp->pal);
- i1480u_rx_release(i1480u);
- i1480u_tx_release(i1480u);
- return 0;
-}
-
-/**
- *
- * Change the interface config--we probably don't have to do anything.
- */
-int i1480u_set_config(struct net_device *net_dev, struct ifmap *map)
-{
- int result;
- struct i1480u *i1480u = netdev_priv(net_dev);
- BUG_ON(i1480u->wlp.rc == NULL);
- result = 0;
- return result;
-}
-
-/**
- * Change the MTU of the interface
- */
-int i1480u_change_mtu(struct net_device *net_dev, int mtu)
-{
- static union {
- struct wlp_tx_hdr tx;
- struct wlp_rx_hdr rx;
- } i1480u_all_hdrs;
-
- if (mtu < ETH_HLEN) /* We encap eth frames */
- return -ERANGE;
- if (mtu > 4000 - sizeof(i1480u_all_hdrs))
- return -ERANGE;
- net_dev->mtu = mtu;
- return 0;
-}
-
-/**
- * Stop the network queue
- *
- * Enable WLP substack to stop network queue. We also set the flow control
- * threshold at this time to prevent the flow control from restarting the
- * queue.
- *
- * we are loosing the current threshold value here ... FIXME?
- */
-void i1480u_stop_queue(struct wlp *wlp)
-{
- struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
- struct net_device *net_dev = i1480u->net_dev;
- i1480u->tx_inflight.threshold = 0;
- netif_stop_queue(net_dev);
-}
-
-/**
- * Start the network queue
- *
- * Enable WLP substack to start network queue. Also re-enable the flow
- * control to manage the queue again.
- *
- * We re-enable the flow control by storing the default threshold in the
- * flow control threshold. This means that if the user modified the
- * threshold before the queue was stopped and restarted that information
- * will be lost. FIXME?
- */
-void i1480u_start_queue(struct wlp *wlp)
-{
- struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
- struct net_device *net_dev = i1480u->net_dev;
- i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
- netif_start_queue(net_dev);
-}
diff --git a/drivers/uwb/i1480/i1480u-wlp/rx.c b/drivers/uwb/i1480/i1480u-wlp/rx.c
deleted file mode 100644
index d4e51e108aa4..000000000000
--- a/drivers/uwb/i1480/i1480u-wlp/rx.c
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * WUSB Wire Adapter: WLP interface
- * Driver for the Linux Network stack.
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * i1480u's RX handling is simple. i1480u will send the received
- * network packets broken up in fragments; 1 to N fragments make a
- * packet, we assemble them together and deliver the packet with netif_rx().
- *
- * Beacuse each USB transfer is a *single* fragment (except when the
- * transfer contains a first fragment), each URB called thus
- * back contains one or two fragments. So we queue N URBs, each with its own
- * fragment buffer. When a URB is done, we process it (adding to the
- * current skb from the fragment buffer until complete). Once
- * processed, we requeue the URB. There is always a bunch of URBs
- * ready to take data, so the intergap should be minimal.
- *
- * An URB's transfer buffer is the data field of a socket buffer. This
- * reduces copying as data can be passed directly to network layer. If a
- * complete packet or 1st fragment is received the URB's transfer buffer is
- * taken away from it and used to send data to the network layer. In this
- * case a new transfer buffer is allocated to the URB before being requeued.
- * If a "NEXT" or "LAST" fragment is received, the fragment contents is
- * appended to the RX packet under construction and the transfer buffer
- * is reused. To be able to use this buffer to assemble complete packets
- * we set each buffer's size to that of the MAX ethernet packet that can
- * be received. There is thus room for improvement in memory usage.
- *
- * When the max tx fragment size increases, we should be able to read
- * data into the skbs directly with very simple code.
- *
- * ROADMAP:
- *
- * ENTRY POINTS:
- *
- * i1480u_rx_setup(): setup RX context [from i1480u_open()]
- *
- * i1480u_rx_release(): release RX context [from i1480u_stop()]
- *
- * i1480u_rx_cb(): called when the RX USB URB receives a
- * packet. It removes the header and pushes it up
- * the Linux netdev stack with netif_rx().
- *
- * i1480u_rx_buffer()
- * i1480u_drop() and i1480u_fix()
- * i1480u_skb_deliver
- *
- */
-
-#include <linux/gfp.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include "i1480u-wlp.h"
-
-/*
- * Setup the RX context
- *
- * Each URB is provided with a transfer_buffer that is the data field
- * of a new socket buffer.
- */
-int i1480u_rx_setup(struct i1480u *i1480u)
-{
- int result, cnt;
- struct device *dev = &i1480u->usb_iface->dev;
- struct net_device *net_dev = i1480u->net_dev;
- struct usb_endpoint_descriptor *epd;
- struct sk_buff *skb;
-
- /* Alloc RX stuff */
- i1480u->rx_skb = NULL; /* not in process of receiving packet */
- result = -ENOMEM;
- epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc;
- for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
- struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt];
- rx_buf->i1480u = i1480u;
- skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
- if (!skb) {
- dev_err(dev,
- "RX: cannot allocate RX buffer %d\n", cnt);
- result = -ENOMEM;
- goto error;
- }
- skb->dev = net_dev;
- skb->ip_summed = CHECKSUM_NONE;
- skb_reserve(skb, 2);
- rx_buf->data = skb;
- rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (unlikely(rx_buf->urb == NULL)) {
- dev_err(dev, "RX: cannot allocate URB %d\n", cnt);
- result = -ENOMEM;
- goto error;
- }
- usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev,
- usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress),
- rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2,
- i1480u_rx_cb, rx_buf);
- result = usb_submit_urb(rx_buf->urb, GFP_NOIO);
- if (unlikely(result < 0)) {
- dev_err(dev, "RX: cannot submit URB %d: %d\n",
- cnt, result);
- goto error;
- }
- }
- return 0;
-
-error:
- i1480u_rx_release(i1480u);
- return result;
-}
-
-
-/* Release resources associated to the rx context */
-void i1480u_rx_release(struct i1480u *i1480u)
-{
- int cnt;
- for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
- if (i1480u->rx_buf[cnt].data)
- dev_kfree_skb(i1480u->rx_buf[cnt].data);
- if (i1480u->rx_buf[cnt].urb) {
- usb_kill_urb(i1480u->rx_buf[cnt].urb);
- usb_free_urb(i1480u->rx_buf[cnt].urb);
- }
- }
- if (i1480u->rx_skb != NULL)
- dev_kfree_skb(i1480u->rx_skb);
-}
-
-static
-void i1480u_rx_unlink_urbs(struct i1480u *i1480u)
-{
- int cnt;
- for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
- if (i1480u->rx_buf[cnt].urb)
- usb_unlink_urb(i1480u->rx_buf[cnt].urb);
- }
-}
-
-/* Fix an out-of-sequence packet */
-#define i1480u_fix(i1480u, msg...) \
-do { \
- if (printk_ratelimit()) \
- dev_err(&i1480u->usb_iface->dev, msg); \
- dev_kfree_skb_irq(i1480u->rx_skb); \
- i1480u->rx_skb = NULL; \
- i1480u->rx_untd_pkt_size = 0; \
-} while (0)
-
-
-/* Drop an out-of-sequence packet */
-#define i1480u_drop(i1480u, msg...) \
-do { \
- if (printk_ratelimit()) \
- dev_err(&i1480u->usb_iface->dev, msg); \
- i1480u->net_dev->stats.rx_dropped++; \
-} while (0)
-
-
-
-
-/* Finalizes setting up the SKB and delivers it
- *
- * We first pass the incoming frame to WLP substack for verification. It
- * may also be a WLP association frame in which case WLP will take over the
- * processing. If WLP does not take it over it will still verify it, if the
- * frame is invalid the skb will be freed by WLP and we will not continue
- * parsing.
- * */
-static
-void i1480u_skb_deliver(struct i1480u *i1480u)
-{
- int should_parse;
- struct net_device *net_dev = i1480u->net_dev;
- struct device *dev = &i1480u->usb_iface->dev;
-
- should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb,
- &i1480u->rx_srcaddr);
- if (!should_parse)
- goto out;
- i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev);
- net_dev->stats.rx_packets++;
- net_dev->stats.rx_bytes += i1480u->rx_untd_pkt_size;
-
- netif_rx(i1480u->rx_skb); /* deliver */
-out:
- i1480u->rx_skb = NULL;
- i1480u->rx_untd_pkt_size = 0;
-}
-
-
-/*
- * Process a buffer of data received from the USB RX endpoint
- *
- * First fragment arrives with next or last fragment. All other fragments
- * arrive alone.
- *
- * /me hates long functions.
- */
-static
-void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf)
-{
- unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */
- size_t untd_hdr_size, untd_frg_size;
- size_t i1480u_hdr_size;
- struct wlp_rx_hdr *i1480u_hdr = NULL;
-
- struct i1480u *i1480u = rx_buf->i1480u;
- struct sk_buff *skb = rx_buf->data;
- int size_left = rx_buf->urb->actual_length;
- void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */
- struct untd_hdr *untd_hdr;
-
- struct net_device *net_dev = i1480u->net_dev;
- struct device *dev = &i1480u->usb_iface->dev;
- struct sk_buff *new_skb;
-
-#if 0
- dev_fnstart(dev,
- "(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left);
- dev_err(dev, "RX packet, %zu bytes\n", size_left);
- dump_bytes(dev, ptr, size_left);
-#endif
- i1480u_hdr_size = sizeof(struct wlp_rx_hdr);
-
- while (size_left > 0) {
- if (pkt_completed) {
- i1480u_drop(i1480u, "RX: fragment follows completed"
- "packet in same buffer. Dropping\n");
- break;
- }
- untd_hdr = ptr;
- if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */
- i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n");
- goto out;
- }
- if (unlikely(untd_hdr_rx_tx(untd_hdr) == 0)) { /* Paranoia: TX set? */
- i1480u_drop(i1480u, "RX: TX bit set! Dropping\n");
- goto out;
- }
- switch (untd_hdr_type(untd_hdr)) { /* Check the UNTD header type */
- case i1480u_PKT_FRAG_1ST: {
- struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr;
- dev_dbg(dev, "1st fragment\n");
- untd_hdr_size = sizeof(struct untd_hdr_1st);
- if (i1480u->rx_skb != NULL)
- i1480u_fix(i1480u, "RX: 1st fragment out of "
- "sequence! Fixing\n");
- if (size_left < untd_hdr_size + i1480u_hdr_size) {
- i1480u_drop(i1480u, "RX: short 1st fragment! "
- "Dropping\n");
- goto out;
- }
- i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len)
- - i1480u_hdr_size;
- untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len);
- if (size_left < untd_hdr_size + untd_frg_size) {
- i1480u_drop(i1480u,
- "RX: short payload! Dropping\n");
- goto out;
- }
- i1480u->rx_skb = skb;
- i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size;
- i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
- skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size);
- skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
- stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
- stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
- rx_buf->data = NULL; /* need to create new buffer */
- break;
- }
- case i1480u_PKT_FRAG_NXT: {
- dev_dbg(dev, "nxt fragment\n");
- untd_hdr_size = sizeof(struct untd_hdr_rst);
- if (i1480u->rx_skb == NULL) {
- i1480u_drop(i1480u, "RX: next fragment out of "
- "sequence! Dropping\n");
- goto out;
- }
- if (size_left < untd_hdr_size) {
- i1480u_drop(i1480u, "RX: short NXT fragment! "
- "Dropping\n");
- goto out;
- }
- untd_frg_size = le16_to_cpu(untd_hdr->len);
- if (size_left < untd_hdr_size + untd_frg_size) {
- i1480u_drop(i1480u,
- "RX: short payload! Dropping\n");
- goto out;
- }
- memmove(skb_put(i1480u->rx_skb, untd_frg_size),
- ptr + untd_hdr_size, untd_frg_size);
- break;
- }
- case i1480u_PKT_FRAG_LST: {
- dev_dbg(dev, "Lst fragment\n");
- untd_hdr_size = sizeof(struct untd_hdr_rst);
- if (i1480u->rx_skb == NULL) {
- i1480u_drop(i1480u, "RX: last fragment out of "
- "sequence! Dropping\n");
- goto out;
- }
- if (size_left < untd_hdr_size) {
- i1480u_drop(i1480u, "RX: short LST fragment! "
- "Dropping\n");
- goto out;
- }
- untd_frg_size = le16_to_cpu(untd_hdr->len);
- if (size_left < untd_frg_size + untd_hdr_size) {
- i1480u_drop(i1480u,
- "RX: short payload! Dropping\n");
- goto out;
- }
- memmove(skb_put(i1480u->rx_skb, untd_frg_size),
- ptr + untd_hdr_size, untd_frg_size);
- pkt_completed = 1;
- break;
- }
- case i1480u_PKT_FRAG_CMP: {
- dev_dbg(dev, "cmp fragment\n");
- untd_hdr_size = sizeof(struct untd_hdr_cmp);
- if (i1480u->rx_skb != NULL)
- i1480u_fix(i1480u, "RX: fix out-of-sequence CMP"
- " fragment!\n");
- if (size_left < untd_hdr_size + i1480u_hdr_size) {
- i1480u_drop(i1480u, "RX: short CMP fragment! "
- "Dropping\n");
- goto out;
- }
- i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len);
- untd_frg_size = i1480u->rx_untd_pkt_size;
- if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) {
- i1480u_drop(i1480u,
- "RX: short payload! Dropping\n");
- goto out;
- }
- i1480u->rx_skb = skb;
- i1480u_hdr = (void *) untd_hdr + untd_hdr_size;
- i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
- stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
- stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
- skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size);
- skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
- rx_buf->data = NULL; /* for hand off skb to network stack */
- pkt_completed = 1;
- i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */
- break;
- }
- default:
- i1480u_drop(i1480u, "RX: unknown packet type %u! "
- "Dropping\n", untd_hdr_type(untd_hdr));
- goto out;
- }
- size_left -= untd_hdr_size + untd_frg_size;
- if (size_left > 0)
- ptr += untd_hdr_size + untd_frg_size;
- }
- if (pkt_completed)
- i1480u_skb_deliver(i1480u);
-out:
- /* recreate needed RX buffers*/
- if (rx_buf->data == NULL) {
- /* buffer is being used to receive packet, create new */
- new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
- if (!new_skb) {
- if (printk_ratelimit())
- dev_err(dev,
- "RX: cannot allocate RX buffer\n");
- } else {
- new_skb->dev = net_dev;
- new_skb->ip_summed = CHECKSUM_NONE;
- skb_reserve(new_skb, 2);
- rx_buf->data = new_skb;
- }
- }
- return;
-}
-
-
-/*
- * Called when an RX URB has finished receiving or has found some kind
- * of error condition.
- *
- * LIMITATIONS:
- *
- * - We read USB-transfers, each transfer contains a SINGLE fragment
- * (can contain a complete packet, or a 1st, next, or last fragment
- * of a packet).
- * Looks like a transfer can contain more than one fragment (07/18/06)
- *
- * - Each transfer buffer is the size of the maximum packet size (minus
- * headroom), i1480u_MAX_PKT_SIZE - 2
- *
- * - We always read the full USB-transfer, no partials.
- *
- * - Each transfer is read directly into a skb. This skb will be used to
- * send data to the upper layers if it is the first fragment or a complete
- * packet. In the other cases the data will be copied from the skb to
- * another skb that is being prepared for the upper layers from a prev
- * first fragment.
- *
- * It is simply too much of a pain. Gosh, there should be a unified
- * SG infrastructure for *everything* [so that I could declare a SG
- * buffer, pass it to USB for receiving, append some space to it if
- * I wish, receive more until I have the whole chunk, adapt
- * pointers on each fragment to remove hardware headers and then
- * attach that to an skbuff and netif_rx()].
- */
-void i1480u_rx_cb(struct urb *urb)
-{
- int result;
- int do_parse_buffer = 1;
- struct i1480u_rx_buf *rx_buf = urb->context;
- struct i1480u *i1480u = rx_buf->i1480u;
- struct device *dev = &i1480u->usb_iface->dev;
- unsigned long flags;
- u8 rx_buf_idx = rx_buf - i1480u->rx_buf;
-
- switch (urb->status) {
- case 0:
- break;
- case -ECONNRESET: /* Not an error, but a controlled situation; */
- case -ENOENT: /* (we killed the URB)...so, no broadcast */
- case -ESHUTDOWN: /* going away! */
- dev_err(dev, "RX URB[%u]: goind down %d\n",
- rx_buf_idx, urb->status);
- goto error;
- default:
- dev_err(dev, "RX URB[%u]: unknown status %d\n",
- rx_buf_idx, urb->status);
- if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS,
- EDC_ERROR_TIMEFRAME)) {
- dev_err(dev, "RX: max acceptable errors exceeded,"
- " resetting device.\n");
- i1480u_rx_unlink_urbs(i1480u);
- wlp_reset_all(&i1480u->wlp);
- goto error;
- }
- do_parse_buffer = 0;
- break;
- }
- spin_lock_irqsave(&i1480u->lock, flags);
- /* chew the data fragments, extract network packets */
- if (do_parse_buffer) {
- i1480u_rx_buffer(rx_buf);
- if (rx_buf->data) {
- rx_buf->urb->transfer_buffer = rx_buf->data->data;
- result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC);
- if (result < 0) {
- dev_err(dev, "RX URB[%u]: cannot submit %d\n",
- rx_buf_idx, result);
- }
- }
- }
- spin_unlock_irqrestore(&i1480u->lock, flags);
-error:
- return;
-}
-
diff --git a/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/drivers/uwb/i1480/i1480u-wlp/sysfs.c
deleted file mode 100644
index 4ffaf546cc6c..000000000000
--- a/drivers/uwb/i1480/i1480u-wlp/sysfs.c
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * WUSB Wire Adapter: WLP interface
- * Sysfs interfaces
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: docs
- */
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/device.h>
-
-#include "i1480u-wlp.h"
-
-
-/**
- *
- * @dev: Class device from the net_device; assumed refcnted.
- *
- * Yes, I don't lock--we assume it is refcounted and I am getting a
- * single byte value that is kind of atomic to read.
- */
-ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf)
-{
- return sprintf(buf, "%u\n",
- wlp_tx_hdr_phy_rate(&options->def_tx_hdr));
-}
-EXPORT_SYMBOL_GPL(uwb_phy_rate_show);
-
-
-ssize_t uwb_phy_rate_store(struct wlp_options *options,
- const char *buf, size_t size)
-{
- ssize_t result;
- unsigned rate;
-
- result = sscanf(buf, "%u\n", &rate);
- if (result != 1) {
- result = -EINVAL;
- goto out;
- }
- result = -EINVAL;
- if (rate >= UWB_PHY_RATE_INVALID)
- goto out;
- wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate);
- result = 0;
-out:
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(uwb_phy_rate_store);
-
-
-ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf)
-{
- return sprintf(buf, "%u\n",
- wlp_tx_hdr_rts_cts(&options->def_tx_hdr));
-}
-EXPORT_SYMBOL_GPL(uwb_rts_cts_show);
-
-
-ssize_t uwb_rts_cts_store(struct wlp_options *options,
- const char *buf, size_t size)
-{
- ssize_t result;
- unsigned value;
-
- result = sscanf(buf, "%u\n", &value);
- if (result != 1) {
- result = -EINVAL;
- goto out;
- }
- result = -EINVAL;
- wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value);
- result = 0;
-out:
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(uwb_rts_cts_store);
-
-
-ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf)
-{
- return sprintf(buf, "%u\n",
- wlp_tx_hdr_ack_policy(&options->def_tx_hdr));
-}
-EXPORT_SYMBOL_GPL(uwb_ack_policy_show);
-
-
-ssize_t uwb_ack_policy_store(struct wlp_options *options,
- const char *buf, size_t size)
-{
- ssize_t result;
- unsigned value;
-
- result = sscanf(buf, "%u\n", &value);
- if (result != 1 || value > UWB_ACK_B_REQ) {
- result = -EINVAL;
- goto out;
- }
- wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value);
- result = 0;
-out:
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(uwb_ack_policy_store);
-
-
-/**
- * Show the PCA base priority.
- *
- * We can access without locking, as the value is (for now) orthogonal
- * to other values.
- */
-ssize_t uwb_pca_base_priority_show(const struct wlp_options *options,
- char *buf)
-{
- return sprintf(buf, "%u\n",
- options->pca_base_priority);
-}
-EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show);
-
-
-/**
- * Set the PCA base priority.
- *
- * We can access without locking, as the value is (for now) orthogonal
- * to other values.
- */
-ssize_t uwb_pca_base_priority_store(struct wlp_options *options,
- const char *buf, size_t size)
-{
- ssize_t result = -EINVAL;
- u8 pca_base_priority;
-
- result = sscanf(buf, "%hhu\n", &pca_base_priority);
- if (result != 1) {
- result = -EINVAL;
- goto out;
- }
- result = -EINVAL;
- if (pca_base_priority >= 8)
- goto out;
- options->pca_base_priority = pca_base_priority;
- /* Update TX header if we are currently using PCA. */
- if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0)
- wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority);
- result = 0;
-out:
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store);
-
-/**
- * Show current inflight values
- *
- * Will print the current MAX and THRESHOLD values for the basic flow
- * control. In addition it will report how many times the TX queue needed
- * to be restarted since the last time this query was made.
- */
-static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight,
- char *buf)
-{
- ssize_t result;
- unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ;
- unsigned long restart_count = atomic_read(&inflight->restart_count);
-
- result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n"
- "#read: threshold max inflight_count restarts "
- "seconds restarts/sec\n"
- "#write: threshold max\n",
- inflight->threshold, inflight->max,
- atomic_read(&inflight->count),
- restart_count, sec_elapsed,
- sec_elapsed == 0 ? 0 : restart_count/sec_elapsed);
- inflight->restart_ts = jiffies;
- atomic_set(&inflight->restart_count, 0);
- return result;
-}
-
-static
-ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight,
- const char *buf, size_t size)
-{
- unsigned long in_threshold, in_max;
- ssize_t result;
- result = sscanf(buf, "%lu %lu", &in_threshold, &in_max);
- if (result != 2)
- return -EINVAL;
- if (in_max <= in_threshold)
- return -EINVAL;
- inflight->max = in_max;
- inflight->threshold = in_threshold;
- return size;
-}
-/*
- * Glue (or function adaptors) for accesing info on sysfs
- *
- * [we need this indirection because the PCI driver does almost the
- * same]
- *
- * Linux 2.6.21 changed how 'struct netdevice' does attributes (from
- * having a 'struct class_dev' to having a 'struct device'). That is
- * quite of a pain.
- *
- * So we try to abstract that here. i1480u_SHOW() and i1480u_STORE()
- * create adaptors for extracting the 'struct i1480u' from a 'struct
- * dev' and calling a function for doing a sysfs operation (as we have
- * them factorized already). i1480u_ATTR creates the attribute file
- * (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a
- * class_device_attr_NAME or device_attr_NAME (for group registration).
- */
-
-#define i1480u_SHOW(name, fn, param) \
-static ssize_t i1480u_show_##name(struct device *dev, \
- struct device_attribute *attr,\
- char *buf) \
-{ \
- struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
- return fn(&i1480u->param, buf); \
-}
-
-#define i1480u_STORE(name, fn, param) \
-static ssize_t i1480u_store_##name(struct device *dev, \
- struct device_attribute *attr,\
- const char *buf, size_t size)\
-{ \
- struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
- return fn(&i1480u->param, buf, size); \
-}
-
-#define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \
- i1480u_show_##name,\
- i1480u_store_##name)
-
-#define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \
- S_IRUGO, \
- i1480u_show_##name, NULL)
-
-#define i1480u_ATTR_NAME(a) (dev_attr_##a)
-
-
-/*
- * Sysfs adaptors
- */
-i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options);
-i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options);
-i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options);
-i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options);
-i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options);
-i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options);
-i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options);
-i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options);
-i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_eda, wlp_eda_show, wlp);
-i1480u_STORE(wlp_eda, wlp_eda_store, wlp);
-i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp);
-i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp);
-i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp);
-i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp);
-i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp);
-i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp);
-i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp);
-i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp);
-i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp);
-i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp);
-i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp);
-i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp);
-i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp);
-i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp);
-i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp);
-i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp);
-i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp);
-i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp);
-i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp);
-i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp);
-i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR);
-
-i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp);
-i1480u_ATTR_SHOW(wlp_neighborhood);
-
-i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss);
-i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss);
-i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR);
-
-/*
- * Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over
- * the last 256 received WLP frames (ECMA-368 13.3).
- *
- * [the -7dB that have to be substracted from the LQI to make the LQE
- * are already taken into account].
- */
-i1480u_SHOW(wlp_lqe, stats_show, lqe_stats);
-i1480u_STORE(wlp_lqe, stats_store, lqe_stats);
-i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR);
-
-/*
- * Show the Receive Signal Strength Indicator averaged over all the
- * received WLP frames (ECMA-368 13.3). Still is not clear what
- * this value is, but is kind of a percentage of the signal strength
- * at the antenna.
- */
-i1480u_SHOW(wlp_rssi, stats_show, rssi_stats);
-i1480u_STORE(wlp_rssi, stats_store, rssi_stats);
-i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR);
-
-/**
- * We maintain a basic flow control counter. "count" how many TX URBs are
- * outstanding. Only allow "max"
- * TX URBs to be outstanding. If this value is reached the queue will be
- * stopped. The queue will be restarted when there are
- * "threshold" URBs outstanding.
- */
-i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight);
-i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight);
-i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR);
-
-static struct attribute *i1480u_attrs[] = {
- &i1480u_ATTR_NAME(uwb_phy_rate).attr,
- &i1480u_ATTR_NAME(uwb_rts_cts).attr,
- &i1480u_ATTR_NAME(uwb_ack_policy).attr,
- &i1480u_ATTR_NAME(uwb_pca_base_priority).attr,
- &i1480u_ATTR_NAME(wlp_lqe).attr,
- &i1480u_ATTR_NAME(wlp_rssi).attr,
- &i1480u_ATTR_NAME(wlp_eda).attr,
- &i1480u_ATTR_NAME(wlp_uuid).attr,
- &i1480u_ATTR_NAME(wlp_dev_name).attr,
- &i1480u_ATTR_NAME(wlp_dev_manufacturer).attr,
- &i1480u_ATTR_NAME(wlp_dev_model_name).attr,
- &i1480u_ATTR_NAME(wlp_dev_model_nr).attr,
- &i1480u_ATTR_NAME(wlp_dev_serial).attr,
- &i1480u_ATTR_NAME(wlp_dev_prim_category).attr,
- &i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr,
- &i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr,
- &i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr,
- &i1480u_ATTR_NAME(wlp_neighborhood).attr,
- &i1480u_ATTR_NAME(wss_activate).attr,
- &i1480u_ATTR_NAME(wlp_tx_inflight).attr,
- NULL,
-};
-
-static struct attribute_group i1480u_attr_group = {
- .name = NULL, /* we want them in the same directory */
- .attrs = i1480u_attrs,
-};
-
-int i1480u_sysfs_setup(struct i1480u *i1480u)
-{
- int result;
- struct device *dev = &i1480u->usb_iface->dev;
- result = sysfs_create_group(&i1480u->net_dev->dev.kobj,
- &i1480u_attr_group);
- if (result < 0)
- dev_err(dev, "cannot initialize sysfs attributes: %d\n",
- result);
- return result;
-}
-
-
-void i1480u_sysfs_release(struct i1480u *i1480u)
-{
- sysfs_remove_group(&i1480u->net_dev->dev.kobj,
- &i1480u_attr_group);
-}
diff --git a/drivers/uwb/i1480/i1480u-wlp/tx.c b/drivers/uwb/i1480/i1480u-wlp/tx.c
deleted file mode 100644
index 3c117a364564..000000000000
--- a/drivers/uwb/i1480/i1480u-wlp/tx.c
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * WUSB Wire Adapter: WLP interface
- * Deal with TX (massaging data to transmit, handling it)
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * Transmission engine. Get an skb, create from that a WLP transmit
- * context, add a WLP TX header (which we keep prefilled in the
- * device's instance), fill out the target-specific fields and
- * fire it.
- *
- * ROADMAP:
- *
- * Entry points:
- *
- * i1480u_tx_release(): called by i1480u_disconnect() to release
- * pending tx contexts.
- *
- * i1480u_tx_cb(): callback for TX contexts (USB URBs)
- * i1480u_tx_destroy():
- *
- * i1480u_tx_timeout(): called for timeout handling from the
- * network stack.
- *
- * i1480u_hard_start_xmit(): called for transmitting an skb from
- * the network stack. Will interact with WLP
- * substack to verify and prepare frame.
- * i1480u_xmit_frame(): actual transmission on hardware
- *
- * i1480u_tx_create() Creates TX context
- * i1480u_tx_create_1() For packets in 1 fragment
- * i1480u_tx_create_n() For packets in >1 fragments
- *
- * TODO:
- *
- * - FIXME: rewrite using usb_sg_*(), add asynch support to
- * usb_sg_*(). It might not make too much sense as most of
- * the times the MTU will be smaller than one page...
- */
-
-#include <linux/slab.h>
-#include "i1480u-wlp.h"
-
-enum {
- /* This is only for Next and Last TX packets */
- i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE
- - sizeof(struct untd_hdr_rst),
-};
-
-/* Free resources allocated to a i1480u tx context. */
-static
-void i1480u_tx_free(struct i1480u_tx *wtx)
-{
- kfree(wtx->buf);
- if (wtx->skb)
- dev_kfree_skb_irq(wtx->skb);
- usb_free_urb(wtx->urb);
- kfree(wtx);
-}
-
-static
-void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx)
-{
- unsigned long flags;
- spin_lock_irqsave(&i1480u->tx_list_lock, flags); /* not active any more */
- list_del(&wtx->list_node);
- i1480u_tx_free(wtx);
- spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
-}
-
-static
-void i1480u_tx_unlink_urbs(struct i1480u *i1480u)
-{
- unsigned long flags;
- struct i1480u_tx *wtx, *next;
-
- spin_lock_irqsave(&i1480u->tx_list_lock, flags);
- list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
- usb_unlink_urb(wtx->urb);
- }
- spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
-}
-
-
-/*
- * Callback for a completed tx USB URB.
- *
- * TODO:
- *
- * - FIXME: recover errors more gracefully
- * - FIXME: handle NAKs (I dont think they come here) for flow ctl
- */
-static
-void i1480u_tx_cb(struct urb *urb)
-{
- struct i1480u_tx *wtx = urb->context;
- struct i1480u *i1480u = wtx->i1480u;
- struct net_device *net_dev = i1480u->net_dev;
- struct device *dev = &i1480u->usb_iface->dev;
- unsigned long flags;
-
- switch (urb->status) {
- case 0:
- spin_lock_irqsave(&i1480u->lock, flags);
- net_dev->stats.tx_packets++;
- net_dev->stats.tx_bytes += urb->actual_length;
- spin_unlock_irqrestore(&i1480u->lock, flags);
- break;
- case -ECONNRESET: /* Not an error, but a controlled situation; */
- case -ENOENT: /* (we killed the URB)...so, no broadcast */
- dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status);
- netif_stop_queue(net_dev);
- break;
- case -ESHUTDOWN: /* going away! */
- dev_dbg(dev, "notif endp: down %d\n", urb->status);
- netif_stop_queue(net_dev);
- break;
- default:
- dev_err(dev, "TX: unknown URB status %d\n", urb->status);
- if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS,
- EDC_ERROR_TIMEFRAME)) {
- dev_err(dev, "TX: max acceptable errors exceeded."
- "Reset device.\n");
- netif_stop_queue(net_dev);
- i1480u_tx_unlink_urbs(i1480u);
- wlp_reset_all(&i1480u->wlp);
- }
- break;
- }
- i1480u_tx_destroy(i1480u, wtx);
- if (atomic_dec_return(&i1480u->tx_inflight.count)
- <= i1480u->tx_inflight.threshold
- && netif_queue_stopped(net_dev)
- && i1480u->tx_inflight.threshold != 0) {
- netif_start_queue(net_dev);
- atomic_inc(&i1480u->tx_inflight.restart_count);
- }
- return;
-}
-
-
-/*
- * Given a buffer that doesn't fit in a single fragment, create an
- * scatter/gather structure for delivery to the USB pipe.
- *
- * Implements functionality of i1480u_tx_create().
- *
- * @wtx: tx descriptor
- * @skb: skb to send
- * @gfp_mask: gfp allocation mask
- * @returns: Pointer to @wtx if ok, NULL on error.
- *
- * Sorry, TOO LONG a function, but breaking it up is kind of hard
- *
- * This will break the buffer in chunks smaller than
- * i1480u_MAX_FRG_SIZE (including the header) and add proper headers
- * to each:
- *
- * 1st header \
- * i1480 tx header | fragment 1
- * fragment data /
- * nxt header \ fragment 2
- * fragment data /
- * ..
- * ..
- * last header \ fragment 3
- * last fragment data /
- *
- * This does not fill the i1480 TX header, it is left up to the
- * caller to do that; you can get it from @wtx->wlp_tx_hdr.
- *
- * This function consumes the skb unless there is an error.
- */
-static
-int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb,
- gfp_t gfp_mask)
-{
- int result;
- void *pl;
- size_t pl_size;
-
- void *pl_itr, *buf_itr;
- size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0;
- struct untd_hdr_1st *untd_hdr_1st;
- struct wlp_tx_hdr *wlp_tx_hdr;
- struct untd_hdr_rst *untd_hdr_rst;
-
- wtx->skb = NULL;
- pl = skb->data;
- pl_itr = pl;
- pl_size = skb->len;
- pl_size_left = pl_size; /* payload size */
- /* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus
- * the headers */
- pl_size_1st = i1480u_MAX_FRG_SIZE
- - sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr);
- BUG_ON(pl_size_1st > pl_size);
- pl_size_left -= pl_size_1st;
- /* The rest have an smaller header (no i1480 TX header). We
- * need to break up the payload in blocks smaller than
- * i1480u_MAX_PL_SIZE (payload excluding header). */
- frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE;
- /* Allocate space for the new buffer. In this new buffer we'll
- * place the headers followed by the data fragment, headers,
- * data fragments, etc..
- */
- result = -ENOMEM;
- wtx->buf_size = sizeof(*untd_hdr_1st)
- + sizeof(*wlp_tx_hdr)
- + frgs * sizeof(*untd_hdr_rst)
- + pl_size;
- wtx->buf = kmalloc(wtx->buf_size, gfp_mask);
- if (wtx->buf == NULL)
- goto error_buf_alloc;
-
- buf_itr = wtx->buf; /* We got the space, let's fill it up */
- /* Fill 1st fragment */
- untd_hdr_1st = buf_itr;
- buf_itr += sizeof(*untd_hdr_1st);
- untd_hdr_set_type(&untd_hdr_1st->hdr, i1480u_PKT_FRAG_1ST);
- untd_hdr_set_rx_tx(&untd_hdr_1st->hdr, 0);
- untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr));
- untd_hdr_1st->fragment_len =
- cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr));
- memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding));
- /* Set up i1480 header info */
- wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr;
- buf_itr += sizeof(*wlp_tx_hdr);
- /* Copy the first fragment */
- memcpy(buf_itr, pl_itr, pl_size_1st);
- pl_itr += pl_size_1st;
- buf_itr += pl_size_1st;
-
- /* Now do each remaining fragment */
- result = -EINVAL;
- while (pl_size_left > 0) {
- if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf
- > wtx->buf_size) {
- printk(KERN_ERR "BUG: no space for header\n");
- goto error_bug;
- }
- untd_hdr_rst = buf_itr;
- buf_itr += sizeof(*untd_hdr_rst);
- if (pl_size_left > i1480u_MAX_PL_SIZE) {
- frg_pl_size = i1480u_MAX_PL_SIZE;
- untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_NXT);
- } else {
- frg_pl_size = pl_size_left;
- untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_LST);
- }
- untd_hdr_set_rx_tx(&untd_hdr_rst->hdr, 0);
- untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size);
- untd_hdr_rst->padding = 0;
- if (buf_itr + frg_pl_size - wtx->buf
- > wtx->buf_size) {
- printk(KERN_ERR "BUG: no space for payload\n");
- goto error_bug;
- }
- memcpy(buf_itr, pl_itr, frg_pl_size);
- buf_itr += frg_pl_size;
- pl_itr += frg_pl_size;
- pl_size_left -= frg_pl_size;
- }
- dev_kfree_skb_irq(skb);
- return 0;
-
-error_bug:
- printk(KERN_ERR
- "BUG: skb %u bytes\n"
- "BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n"
- "BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n",
- skb->len,
- frg_pl_size, i1480u_MAX_FRG_SIZE,
- buf_itr - wtx->buf, wtx->buf_size, pl_size_left);
-
- kfree(wtx->buf);
-error_buf_alloc:
- return result;
-}
-
-
-/*
- * Given a buffer that fits in a single fragment, fill out a @wtx
- * struct for transmitting it down the USB pipe.
- *
- * Uses the fact that we have space reserved in front of the skbuff
- * for hardware headers :]
- *
- * This does not fill the i1480 TX header, it is left up to the
- * caller to do that; you can get it from @wtx->wlp_tx_hdr.
- *
- * @pl: pointer to payload data
- * @pl_size: size of the payuload
- *
- * This function does not consume the @skb.
- */
-static
-int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb,
- gfp_t gfp_mask)
-{
- struct untd_hdr_cmp *untd_hdr_cmp;
- struct wlp_tx_hdr *wlp_tx_hdr;
-
- wtx->buf = NULL;
- wtx->skb = skb;
- BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr));
- wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr));
- wtx->wlp_tx_hdr = wlp_tx_hdr;
- BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp));
- untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp));
-
- untd_hdr_set_type(&untd_hdr_cmp->hdr, i1480u_PKT_FRAG_CMP);
- untd_hdr_set_rx_tx(&untd_hdr_cmp->hdr, 0);
- untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp));
- untd_hdr_cmp->padding = 0;
- return 0;
-}
-
-
-/*
- * Given a skb to transmit, massage it to become palatable for the TX pipe
- *
- * This will break the buffer in chunks smaller than
- * i1480u_MAX_FRG_SIZE and add proper headers to each.
- *
- * 1st header \
- * i1480 tx header | fragment 1
- * fragment data /
- * nxt header \ fragment 2
- * fragment data /
- * ..
- * ..
- * last header \ fragment 3
- * last fragment data /
- *
- * Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE.
- *
- * If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the
- * following is composed:
- *
- * complete header \
- * i1480 tx header | single fragment
- * packet data /
- *
- * We were going to use s/g support, but because the interface is
- * synch and at the end there is plenty of overhead to do it, it
- * didn't seem that worth for data that is going to be smaller than
- * one page.
- */
-static
-struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u,
- struct sk_buff *skb, gfp_t gfp_mask)
-{
- int result;
- struct usb_endpoint_descriptor *epd;
- int usb_pipe;
- unsigned long flags;
-
- struct i1480u_tx *wtx;
- const size_t pl_max_size =
- i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp)
- - sizeof(struct wlp_tx_hdr);
-
- wtx = kmalloc(sizeof(*wtx), gfp_mask);
- if (wtx == NULL)
- goto error_wtx_alloc;
- wtx->urb = usb_alloc_urb(0, gfp_mask);
- if (wtx->urb == NULL)
- goto error_urb_alloc;
- epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc;
- usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress);
- /* Fits in a single complete packet or need to split? */
- if (skb->len > pl_max_size) {
- result = i1480u_tx_create_n(wtx, skb, gfp_mask);
- if (result < 0)
- goto error_create;
- usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
- wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx);
- } else {
- result = i1480u_tx_create_1(wtx, skb, gfp_mask);
- if (result < 0)
- goto error_create;
- usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
- skb->data, skb->len, i1480u_tx_cb, wtx);
- }
- spin_lock_irqsave(&i1480u->tx_list_lock, flags);
- list_add(&wtx->list_node, &i1480u->tx_list);
- spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
- return wtx;
-
-error_create:
- kfree(wtx->urb);
-error_urb_alloc:
- kfree(wtx);
-error_wtx_alloc:
- return NULL;
-}
-
-/*
- * Actual fragmentation and transmission of frame
- *
- * @wlp: WLP substack data structure
- * @skb: To be transmitted
- * @dst: Device address of destination
- * @returns: 0 on success, <0 on failure
- *
- * This function can also be called directly (not just from
- * hard_start_xmit), so we also check here if the interface is up before
- * taking sending anything.
- */
-int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb,
- struct uwb_dev_addr *dst)
-{
- int result = -ENXIO;
- struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
- struct device *dev = &i1480u->usb_iface->dev;
- struct net_device *net_dev = i1480u->net_dev;
- struct i1480u_tx *wtx;
- struct wlp_tx_hdr *wlp_tx_hdr;
- static unsigned char dev_bcast[2] = { 0xff, 0xff };
-
- BUG_ON(i1480u->wlp.rc == NULL);
- if ((net_dev->flags & IFF_UP) == 0)
- goto out;
- result = -EBUSY;
- if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) {
- netif_stop_queue(net_dev);
- goto error_max_inflight;
- }
- result = -ENOMEM;
- wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC);
- if (unlikely(wtx == NULL)) {
- if (printk_ratelimit())
- dev_err(dev, "TX: no memory for WLP TX URB,"
- "dropping packet (in flight %d)\n",
- atomic_read(&i1480u->tx_inflight.count));
- netif_stop_queue(net_dev);
- goto error_wtx_alloc;
- }
- wtx->i1480u = i1480u;
- /* Fill out the i1480 header; @i1480u->def_tx_hdr read without
- * locking. We do so because they are kind of orthogonal to
- * each other (and thus not changed in an atomic batch).
- * The ETH header is right after the WLP TX header. */
- wlp_tx_hdr = wtx->wlp_tx_hdr;
- *wlp_tx_hdr = i1480u->options.def_tx_hdr;
- wlp_tx_hdr->dstaddr = *dst;
- if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast))
- && (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)) {
- /*Broadcast message directed to DRP host. Send as best effort
- * on PCA. */
- wlp_tx_hdr_set_delivery_id_type(wlp_tx_hdr, i1480u->options.pca_base_priority);
- }
-
- result = usb_submit_urb(wtx->urb, GFP_ATOMIC); /* Go baby */
- if (result < 0) {
- dev_err(dev, "TX: cannot submit URB: %d\n", result);
- /* We leave the freeing of skb to calling function */
- wtx->skb = NULL;
- goto error_tx_urb_submit;
- }
- atomic_inc(&i1480u->tx_inflight.count);
- net_dev->trans_start = jiffies;
- return result;
-
-error_tx_urb_submit:
- i1480u_tx_destroy(i1480u, wtx);
-error_wtx_alloc:
-error_max_inflight:
-out:
- return result;
-}
-
-
-/*
- * Transmit an skb Called when an skbuf has to be transmitted
- *
- * The skb is first passed to WLP substack to ensure this is a valid
- * frame. If valid the device address of destination will be filled and
- * the WLP header prepended to the skb. If this step fails we fake sending
- * the frame, if we return an error the network stack will just keep trying.
- *
- * Broadcast frames inside a WSS needs to be treated special as multicast is
- * not supported. A broadcast frame is sent as unicast to each member of the
- * WSS - this is done by the WLP substack when it finds a broadcast frame.
- * So, we test if the WLP substack took over the skb and only transmit it
- * if it has not (been taken over).
- *
- * @net_dev->xmit_lock is held
- */
-netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *skb,
- struct net_device *net_dev)
-{
- int result;
- struct i1480u *i1480u = netdev_priv(net_dev);
- struct device *dev = &i1480u->usb_iface->dev;
- struct uwb_dev_addr dst;
-
- if ((net_dev->flags & IFF_UP) == 0)
- goto error;
- result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst);
- if (result < 0) {
- dev_err(dev, "WLP verification of TX frame failed (%d). "
- "Dropping packet.\n", result);
- goto error;
- } else if (result == 1) {
- /* trans_start time will be set when WLP actually transmits
- * the frame */
- goto out;
- }
- result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst);
- if (result < 0) {
- dev_err(dev, "Frame TX failed (%d).\n", result);
- goto error;
- }
- return NETDEV_TX_OK;
-error:
- dev_kfree_skb_any(skb);
- net_dev->stats.tx_dropped++;
-out:
- return NETDEV_TX_OK;
-}
-
-
-/*
- * Called when a pkt transmission doesn't complete in a reasonable period
- * Device reset may sleep - do it outside of interrupt context (delayed)
- */
-void i1480u_tx_timeout(struct net_device *net_dev)
-{
- struct i1480u *i1480u = netdev_priv(net_dev);
-
- wlp_reset_all(&i1480u->wlp);
-}
-
-
-void i1480u_tx_release(struct i1480u *i1480u)
-{
- unsigned long flags;
- struct i1480u_tx *wtx, *next;
- int count = 0, empty;
-
- spin_lock_irqsave(&i1480u->tx_list_lock, flags);
- list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
- count++;
- usb_unlink_urb(wtx->urb);
- }
- spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
- count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */
- /*
- * We don't like this sollution too much (dirty as it is), but
- * it is cheaper than putting a refcount on each i1480u_tx and
- * i1480uting for all of them to go away...
- *
- * Called when no more packets can be added to tx_list
- * so can i1480ut for it to be empty.
- */
- while (1) {
- spin_lock_irqsave(&i1480u->tx_list_lock, flags);
- empty = list_empty(&i1480u->tx_list);
- spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
- if (empty)
- break;
- count--;
- BUG_ON(count == 0);
- msleep(20);
- }
-}
diff --git a/drivers/uwb/wlp/Makefile b/drivers/uwb/wlp/Makefile
deleted file mode 100644
index c72c11db5b1b..000000000000
--- a/drivers/uwb/wlp/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-obj-$(CONFIG_UWB_WLP) := wlp.o
-
-wlp-objs := \
- driver.o \
- eda.o \
- messages.o \
- sysfs.o \
- txrx.o \
- wlp-lc.o \
- wss-lc.o
diff --git a/drivers/uwb/wlp/driver.c b/drivers/uwb/wlp/driver.c
deleted file mode 100644
index cb8d699b6a67..000000000000
--- a/drivers/uwb/wlp/driver.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * WiMedia Logical Link Control Protocol (WLP)
- *
- * Copyright (C) 2007 Intel Corporation
- * Reinette Chatre <reinette.chatre@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * Life cycle of WLP substack
- *
- * FIXME: Docs
- */
-
-#include <linux/module.h>
-
-static int __init wlp_subsys_init(void)
-{
- return 0;
-}
-module_init(wlp_subsys_init);
-
-static void __exit wlp_subsys_exit(void)
-{
- return;
-}
-module_exit(wlp_subsys_exit);
-
-MODULE_AUTHOR("Reinette Chatre <reinette.chatre@intel.com>");
-MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)");
-MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/wlp/eda.c b/drivers/uwb/wlp/eda.c
deleted file mode 100644
index 086fc0cf9401..000000000000
--- a/drivers/uwb/wlp/eda.c
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * WUSB Wire Adapter: WLP interface
- * Ethernet to device address cache
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * We need to be able to map ethernet addresses to device addresses
- * and back because there is not explicit relationship between the eth
- * addresses used in the ETH frames and the device addresses (no, it
- * would not have been simpler to force as ETH address the MBOA MAC
- * address...no, not at all :).
- *
- * A device has one MBOA MAC address and one device address. It is possible
- * for a device to have more than one virtual MAC address (although a
- * virtual address can be the same as the MBOA MAC address). The device
- * address is guaranteed to be unique among the devices in the extended
- * beacon group (see ECMA 17.1.1). We thus use the device address as index
- * to this cache. We do allow searching based on virtual address as this
- * is how Ethernet frames will be addressed.
- *
- * We need to support virtual EUI-48. Although, right now the virtual
- * EUI-48 will always be the same as the MAC SAP address. The EDA cache
- * entry thus contains a MAC SAP address as well as the virtual address
- * (used to map the network stack address to a neighbor). When we move
- * to support more than one virtual MAC on a host then this organization
- * will have to change. Perhaps a neighbor has a list of WSSs, each with a
- * tag and virtual EUI-48.
- *
- * On data transmission
- * it is used to determine if the neighbor is connected and what WSS it
- * belongs to. With this we know what tag to add to the WLP frame. Storing
- * the WSS in the EDA cache may be overkill because we only support one
- * WSS. Hopefully we will support more than one WSS at some point.
- * On data reception it is used to determine the WSS based on
- * the tag and address of the transmitting neighbor.
- */
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/slab.h>
-#include <linux/wlp.h>
-#include "wlp-internal.h"
-
-
-/* FIXME: cache is not purged, only on device close */
-
-/* FIXME: does not scale, change to dynamic array */
-
-/*
- * Initialize the EDA cache
- *
- * @returns 0 if ok, < 0 errno code on error
- *
- * Call when the interface is being brought up
- *
- * NOTE: Keep it as a separate function as the implementation will
- * change and be more complex.
- */
-void wlp_eda_init(struct wlp_eda *eda)
-{
- INIT_LIST_HEAD(&eda->cache);
- spin_lock_init(&eda->lock);
-}
-
-/*
- * Release the EDA cache
- *
- * @returns 0 if ok, < 0 errno code on error
- *
- * Called when the interface is brought down
- */
-void wlp_eda_release(struct wlp_eda *eda)
-{
- unsigned long flags;
- struct wlp_eda_node *itr, *next;
-
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
- list_del(&itr->list_node);
- kfree(itr);
- }
- spin_unlock_irqrestore(&eda->lock, flags);
-}
-
-/*
- * Add an address mapping
- *
- * @returns 0 if ok, < 0 errno code on error
- *
- * An address mapping is initially created when the neighbor device is seen
- * for the first time (it is "onair"). At this time the neighbor is not
- * connected or associated with a WSS so we only populate the Ethernet and
- * Device address fields.
- *
- */
-int wlp_eda_create_node(struct wlp_eda *eda,
- const unsigned char eth_addr[ETH_ALEN],
- const struct uwb_dev_addr *dev_addr)
-{
- int result = 0;
- struct wlp_eda_node *itr;
- unsigned long flags;
-
- BUG_ON(dev_addr == NULL || eth_addr == NULL);
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry(itr, &eda->cache, list_node) {
- if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
- printk(KERN_ERR "EDA cache already contains entry "
- "for neighbor %02x:%02x\n",
- dev_addr->data[1], dev_addr->data[0]);
- result = -EEXIST;
- goto out_unlock;
- }
- }
- itr = kzalloc(sizeof(*itr), GFP_ATOMIC);
- if (itr != NULL) {
- memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr));
- itr->dev_addr = *dev_addr;
- list_add(&itr->list_node, &eda->cache);
- } else
- result = -ENOMEM;
-out_unlock:
- spin_unlock_irqrestore(&eda->lock, flags);
- return result;
-}
-
-/*
- * Remove entry from EDA cache
- *
- * This is done when the device goes off air.
- */
-void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr)
-{
- struct wlp_eda_node *itr, *next;
- unsigned long flags;
-
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
- if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
- list_del(&itr->list_node);
- kfree(itr);
- break;
- }
- }
- spin_unlock_irqrestore(&eda->lock, flags);
-}
-
-/*
- * Update an address mapping
- *
- * @returns 0 if ok, < 0 errno code on error
- */
-int wlp_eda_update_node(struct wlp_eda *eda,
- const struct uwb_dev_addr *dev_addr,
- struct wlp_wss *wss,
- const unsigned char virt_addr[ETH_ALEN],
- const u8 tag, const enum wlp_wss_connect state)
-{
- int result = -ENOENT;
- struct wlp_eda_node *itr;
- unsigned long flags;
-
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry(itr, &eda->cache, list_node) {
- if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
- /* Found it, update it */
- itr->wss = wss;
- memcpy(itr->virt_addr, virt_addr,
- sizeof(itr->virt_addr));
- itr->tag = tag;
- itr->state = state;
- result = 0;
- goto out_unlock;
- }
- }
- /* Not found */
-out_unlock:
- spin_unlock_irqrestore(&eda->lock, flags);
- return result;
-}
-
-/*
- * Update only state field of an address mapping
- *
- * @returns 0 if ok, < 0 errno code on error
- */
-int wlp_eda_update_node_state(struct wlp_eda *eda,
- const struct uwb_dev_addr *dev_addr,
- const enum wlp_wss_connect state)
-{
- int result = -ENOENT;
- struct wlp_eda_node *itr;
- unsigned long flags;
-
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry(itr, &eda->cache, list_node) {
- if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
- /* Found it, update it */
- itr->state = state;
- result = 0;
- goto out_unlock;
- }
- }
- /* Not found */
-out_unlock:
- spin_unlock_irqrestore(&eda->lock, flags);
- return result;
-}
-
-/*
- * Return contents of EDA cache entry
- *
- * @dev_addr: index to EDA cache
- * @eda_entry: pointer to where contents of EDA cache will be copied
- */
-int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr,
- struct wlp_eda_node *eda_entry)
-{
- int result = -ENOENT;
- struct wlp_eda_node *itr;
- unsigned long flags;
-
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry(itr, &eda->cache, list_node) {
- if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
- *eda_entry = *itr;
- result = 0;
- goto out_unlock;
- }
- }
- /* Not found */
-out_unlock:
- spin_unlock_irqrestore(&eda->lock, flags);
- return result;
-}
-
-/*
- * Execute function for every element in the cache
- *
- * @function: function to execute on element of cache (must be atomic)
- * @priv: private data of function
- * @returns: result of first function that failed, or last function
- * executed if no function failed.
- *
- * Stop executing when function returns error for any element in cache.
- *
- * IMPORTANT: We are using a spinlock here: the function executed on each
- * element has to be atomic.
- */
-int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function,
- void *priv)
-{
- int result = 0;
- struct wlp *wlp = container_of(eda, struct wlp, eda);
- struct wlp_eda_node *entry;
- unsigned long flags;
-
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry(entry, &eda->cache, list_node) {
- result = (*function)(wlp, entry, priv);
- if (result < 0)
- break;
- }
- spin_unlock_irqrestore(&eda->lock, flags);
- return result;
-}
-
-/*
- * Execute function for single element in the cache (return dev addr)
- *
- * @virt_addr: index into EDA cache used to determine which element to
- * execute the function on
- * @dev_addr: device address of element in cache will be returned using
- * @dev_addr
- * @function: function to execute on element of cache (must be atomic)
- * @priv: private data of function
- * @returns: result of function
- *
- * IMPORTANT: We are using a spinlock here: the function executed on the
- * element has to be atomic.
- */
-int wlp_eda_for_virtual(struct wlp_eda *eda,
- const unsigned char virt_addr[ETH_ALEN],
- struct uwb_dev_addr *dev_addr,
- wlp_eda_for_each_f function,
- void *priv)
-{
- int result = 0;
- struct wlp *wlp = container_of(eda, struct wlp, eda);
- struct wlp_eda_node *itr;
- unsigned long flags;
- int found = 0;
-
- spin_lock_irqsave(&eda->lock, flags);
- list_for_each_entry(itr, &eda->cache, list_node) {
- if (!memcmp(itr->virt_addr, virt_addr,
- sizeof(itr->virt_addr))) {
- result = (*function)(wlp, itr, priv);
- *dev_addr = itr->dev_addr;
- found = 1;
- break;
- }
- }
- if (!found)
- result = -ENODEV;
- spin_unlock_irqrestore(&eda->lock, flags);
- return result;
-}
-
-static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED",
- "WLP_WSS_CONNECTED",
- "WLP_WSS_CONNECT_FAILED",
-};
-
-static const char *wlp_wss_connect_state_str(unsigned id)
-{
- if (id >= ARRAY_SIZE(__wlp_wss_connect_state))
- return "unknown WSS connection state";
- return __wlp_wss_connect_state[id];
-}
-
-/*
- * View EDA cache from user space
- *
- * A debugging feature to give user visibility into the EDA cache. Also
- * used to display members of WSS to user (called from wlp_wss_members_show())
- */
-ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
-{
- ssize_t result = 0;
- struct wlp_eda_node *entry;
- unsigned long flags;
- struct wlp_eda *eda = &wlp->eda;
- spin_lock_irqsave(&eda->lock, flags);
- result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr "
- "tag state virt_addr\n");
- list_for_each_entry(entry, &eda->cache, list_node) {
- result += scnprintf(buf + result, PAGE_SIZE - result,
- "%pM %02x:%02x %p 0x%02x %s %pM\n",
- entry->eth_addr,
- entry->dev_addr.data[1],
- entry->dev_addr.data[0], entry->wss,
- entry->tag,
- wlp_wss_connect_state_str(entry->state),
- entry->virt_addr);
- if (result >= PAGE_SIZE)
- break;
- }
- spin_unlock_irqrestore(&eda->lock, flags);
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_eda_show);
-
-/*
- * Add new EDA cache entry based on user input in sysfs
- *
- * Should only be used for debugging.
- *
- * The WSS is assumed to be the only WSS supported. This needs to be
- * redesigned when we support more than one WSS.
- */
-ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size)
-{
- ssize_t result;
- struct wlp_eda *eda = &wlp->eda;
- u8 eth_addr[6];
- struct uwb_dev_addr dev_addr;
- u8 tag;
- unsigned state;
-
- result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx "
- "%02hhx:%02hhx %02hhx %u\n",
- &eth_addr[0], &eth_addr[1],
- &eth_addr[2], &eth_addr[3],
- &eth_addr[4], &eth_addr[5],
- &dev_addr.data[1], &dev_addr.data[0], &tag, &state);
- switch (result) {
- case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */
- /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/
- result = -ENOSYS;
- break;
- case 10:
- state = state >= 1 ? 1 : 0;
- result = wlp_eda_create_node(eda, eth_addr, &dev_addr);
- if (result < 0 && result != -EEXIST)
- goto error;
- /* Set virtual addr to be same as MAC */
- result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss,
- eth_addr, tag, state);
- if (result < 0)
- goto error;
- break;
- default: /* bad format */
- result = -EINVAL;
- }
-error:
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(wlp_eda_store);
diff --git a/drivers/uwb/wlp/messages.c b/drivers/uwb/wlp/messages.c
deleted file mode 100644
index 3a8e033dce21..000000000000
--- a/drivers/uwb/wlp/messages.c
+++ /dev/null
@@ -1,1798 +0,0 @@
-/*
- * WiMedia Logical Link Control Protocol (WLP)
- * Message construction and parsing
- *
- * Copyright (C) 2007 Intel Corporation
- * Reinette Chatre <reinette.chatre@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: docs
- */
-
-#include <linux/wlp.h>
-#include <linux/slab.h>
-
-#include "wlp-internal.h"
-
-static
-const char *__wlp_assoc_frame[] = {
- [WLP_ASSOC_D1] = "WLP_ASSOC_D1",
- [WLP_ASSOC_D2] = "WLP_ASSOC_D2",
- [WLP_ASSOC_M1] = "WLP_ASSOC_M1",
- [WLP_ASSOC_M2] = "WLP_ASSOC_M2",
- [WLP_ASSOC_M3] = "WLP_ASSOC_M3",
- [WLP_ASSOC_M4] = "WLP_ASSOC_M4",
- [WLP_ASSOC_M5] = "WLP_ASSOC_M5",
- [WLP_ASSOC_M6] = "WLP_ASSOC_M6",
- [WLP_ASSOC_M7] = "WLP_ASSOC_M7",
- [WLP_ASSOC_M8] = "WLP_ASSOC_M8",
- [WLP_ASSOC_F0] = "WLP_ASSOC_F0",
- [WLP_ASSOC_E1] = "WLP_ASSOC_E1",
- [WLP_ASSOC_E2] = "WLP_ASSOC_E2",
- [WLP_ASSOC_C1] = "WLP_ASSOC_C1",
- [WLP_ASSOC_C2] = "WLP_ASSOC_C2",
- [WLP_ASSOC_C3] = "WLP_ASSOC_C3",
- [WLP_ASSOC_C4] = "WLP_ASSOC_C4",
-};
-
-static const char *wlp_assoc_frame_str(unsigned id)
-{
- if (id >= ARRAY_SIZE(__wlp_assoc_frame))
- return "unknown association frame";
- return __wlp_assoc_frame[id];
-}
-
-static const char *__wlp_assc_error[] = {
- "none",
- "Authenticator Failure",
- "Rogue activity suspected",
- "Device busy",
- "Setup Locked",
- "Registrar not ready",
- "Invalid WSS selection",
- "Message timeout",
- "Enrollment session timeout",
- "Device password invalid",
- "Unsupported version",
- "Internal error",
- "Undefined error",
- "Numeric comparison failure",
- "Waiting for user input",
-};
-
-static const char *wlp_assc_error_str(unsigned id)
-{
- if (id >= ARRAY_SIZE(__wlp_assc_error))
- return "unknown WLP association error";
- return __wlp_assc_error[id];
-}
-
-static inline void wlp_set_attr_hdr(struct wlp_attr_hdr *hdr, unsigned type,
- size_t len)
-{
- hdr->type = cpu_to_le16(type);
- hdr->length = cpu_to_le16(len);
-}
-
-/*
- * Populate fields of a constant sized attribute
- *
- * @returns: total size of attribute including size of new value
- *
- * We have two instances of this function (wlp_pset and wlp_set): one takes
- * the value as a parameter, the other takes a pointer to the value as
- * parameter. They thus only differ in how the value is assigned to the
- * attribute.
- *
- * We use sizeof(*attr) - sizeof(struct wlp_attr_hdr) instead of
- * sizeof(type) to be able to use this same code for the structures that
- * contain 8bit enum values and be able to deal with pointer types.
- */
-#define wlp_set(type, type_code, name) \
-static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \
-{ \
- wlp_set_attr_hdr(&attr->hdr, type_code, \
- sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \
- attr->name = value; \
- return sizeof(*attr); \
-}
-
-#define wlp_pset(type, type_code, name) \
-static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \
-{ \
- wlp_set_attr_hdr(&attr->hdr, type_code, \
- sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \
- attr->name = *value; \
- return sizeof(*attr); \
-}
-
-/**
- * Populate fields of a variable attribute
- *
- * @returns: total size of attribute including size of new value
- *
- * Provided with a pointer to the memory area reserved for the
- * attribute structure, the field is populated with the value. The
- * reserved memory has to contain enough space for the value.
- */
-#define wlp_vset(type, type_code, name) \
-static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value, \
- size_t len) \
-{ \
- wlp_set_attr_hdr(&attr->hdr, type_code, len); \
- memcpy(attr->name, value, len); \
- return sizeof(*attr) + len; \
-}
-
-wlp_vset(char *, WLP_ATTR_DEV_NAME, dev_name)
-wlp_vset(char *, WLP_ATTR_MANUF, manufacturer)
-wlp_set(enum wlp_assoc_type, WLP_ATTR_MSG_TYPE, msg_type)
-wlp_vset(char *, WLP_ATTR_MODEL_NAME, model_name)
-wlp_vset(char *, WLP_ATTR_MODEL_NR, model_nr)
-wlp_vset(char *, WLP_ATTR_SERIAL, serial)
-wlp_vset(char *, WLP_ATTR_WSS_NAME, wss_name)
-wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_E, uuid_e)
-wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_R, uuid_r)
-wlp_pset(struct wlp_uuid *, WLP_ATTR_WSSID, wssid)
-wlp_pset(struct wlp_dev_type *, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
-/*wlp_pset(struct wlp_dev_type *, WLP_ATTR_SEC_DEV_TYPE, sec_dev_type)*/
-wlp_set(u8, WLP_ATTR_WLP_VER, version)
-wlp_set(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
-wlp_set(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
-wlp_set(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
-wlp_set(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
-wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_BCAST, wss_bcast)
-wlp_pset(struct wlp_nonce *, WLP_ATTR_ENRL_NONCE, enonce)
-wlp_pset(struct wlp_nonce *, WLP_ATTR_REG_NONCE, rnonce)
-wlp_set(u8, WLP_ATTR_WSS_TAG, wss_tag)
-wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_VIRT, wss_virt)
-
-/**
- * Fill in the WSS information attributes
- *
- * We currently only support one WSS, and this is assumed in this function
- * that can populate only one WSS information attribute.
- */
-static size_t wlp_set_wss_info(struct wlp_attr_wss_info *attr,
- struct wlp_wss *wss)
-{
- size_t datalen;
- void *ptr = attr->wss_info;
- size_t used = sizeof(*attr);
-
- datalen = sizeof(struct wlp_wss_info) + strlen(wss->name);
- wlp_set_attr_hdr(&attr->hdr, WLP_ATTR_WSS_INFO, datalen);
- used = wlp_set_wssid(ptr, &wss->wssid);
- used += wlp_set_wss_name(ptr + used, wss->name, strlen(wss->name));
- used += wlp_set_accept_enrl(ptr + used, wss->accept_enroll);
- used += wlp_set_wss_sec_status(ptr + used, wss->secure_status);
- used += wlp_set_wss_bcast(ptr + used, &wss->bcast);
- return sizeof(*attr) + used;
-}
-
-/**
- * Verify attribute header
- *
- * @hdr: Pointer to attribute header that will be verified.
- * @type: Expected attribute type.
- * @len: Expected length of attribute value (excluding header).
- *
- * Most attribute values have a known length even when they do have a
- * length field. This knowledge can be used via this function to verify
- * that the length field matches the expected value.
- */
-static int wlp_check_attr_hdr(struct wlp *wlp, struct wlp_attr_hdr *hdr,
- enum wlp_attr_type type, unsigned len)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
-
- if (le16_to_cpu(hdr->type) != type) {
- dev_err(dev, "WLP: unexpected header type. Expected "
- "%u, got %u.\n", type, le16_to_cpu(hdr->type));
- return -EINVAL;
- }
- if (le16_to_cpu(hdr->length) != len) {
- dev_err(dev, "WLP: unexpected length in header. Expected "
- "%u, got %u.\n", len, le16_to_cpu(hdr->length));
- return -EINVAL;
- }
- return 0;
-}
-
-/**
- * Check if header of WSS information attribute valid
- *
- * @returns: length of WSS attributes (value of length attribute field) if
- * valid WSS information attribute found
- * -ENODATA if no WSS information attribute found
- * -EIO other error occured
- *
- * The WSS information attribute is optional. The function will be provided
- * with a pointer to data that could _potentially_ be a WSS information
- * attribute. If a valid WSS information attribute is found it will return
- * 0, if no WSS information attribute is found it will return -ENODATA, and
- * another error will be returned if it is a WSS information attribute, but
- * some parsing failure occured.
- */
-static int wlp_check_wss_info_attr_hdr(struct wlp *wlp,
- struct wlp_attr_hdr *hdr, size_t buflen)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- size_t len;
- int result = 0;
-
- if (buflen < sizeof(*hdr)) {
- dev_err(dev, "WLP: Not enough space in buffer to parse"
- " WSS information attribute header.\n");
- result = -EIO;
- goto out;
- }
- if (le16_to_cpu(hdr->type) != WLP_ATTR_WSS_INFO) {
- /* WSS information is optional */
- result = -ENODATA;
- goto out;
- }
- len = le16_to_cpu(hdr->length);
- if (buflen < sizeof(*hdr) + len) {
- dev_err(dev, "WLP: Not enough space in buffer to parse "
- "variable data. Got %d, expected %d.\n",
- (int)buflen, (int)(sizeof(*hdr) + len));
- result = -EIO;
- goto out;
- }
- result = len;
-out:
- return result;
-}
-
-
-static ssize_t wlp_get_attribute(struct wlp *wlp, u16 type_code,
- struct wlp_attr_hdr *attr_hdr, void *value, ssize_t value_len,
- ssize_t buflen)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- ssize_t attr_len = sizeof(*attr_hdr) + value_len;
- if (buflen < 0)
- return -EINVAL;
- if (buflen < attr_len) {
- dev_err(dev, "WLP: Not enough space in buffer to parse"
- " attribute field. Need %d, received %zu\n",
- (int)attr_len, buflen);
- return -EIO;
- }
- if (wlp_check_attr_hdr(wlp, attr_hdr, type_code, value_len) < 0) {
- dev_err(dev, "WLP: Header verification failed. \n");
- return -EINVAL;
- }
- memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), value_len);
- return attr_len;
-}
-
-static ssize_t wlp_vget_attribute(struct wlp *wlp, u16 type_code,
- struct wlp_attr_hdr *attr_hdr, void *value, ssize_t max_value_len,
- ssize_t buflen)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- size_t len;
- if (buflen < 0)
- return -EINVAL;
- if (buflen < sizeof(*attr_hdr)) {
- dev_err(dev, "WLP: Not enough space in buffer to parse"
- " header.\n");
- return -EIO;
- }
- if (le16_to_cpu(attr_hdr->type) != type_code) {
- dev_err(dev, "WLP: Unexpected attribute type. Got %u, "
- "expected %u.\n", le16_to_cpu(attr_hdr->type),
- type_code);
- return -EINVAL;
- }
- len = le16_to_cpu(attr_hdr->length);
- if (len > max_value_len) {
- dev_err(dev, "WLP: Attribute larger than maximum "
- "allowed. Received %zu, max is %d.\n", len,
- (int)max_value_len);
- return -EFBIG;
- }
- if (buflen < sizeof(*attr_hdr) + len) {
- dev_err(dev, "WLP: Not enough space in buffer to parse "
- "variable data.\n");
- return -EIO;
- }
- memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), len);
- return sizeof(*attr_hdr) + len;
-}
-
-/**
- * Get value of attribute from fixed size attribute field.
- *
- * @attr: Pointer to attribute field.
- * @value: Pointer to variable in which attribute value will be placed.
- * @buflen: Size of buffer in which attribute field (including header)
- * can be found.
- * @returns: Amount of given buffer consumed by parsing for this attribute.
- *
- * The size and type of the value is known by the type of the attribute.
- */
-#define wlp_get(type, type_code, name) \
-ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr, \
- type *value, ssize_t buflen) \
-{ \
- return wlp_get_attribute(wlp, (type_code), &attr->hdr, \
- value, sizeof(*value), buflen); \
-}
-
-#define wlp_get_sparse(type, type_code, name) \
- static wlp_get(type, type_code, name)
-
-/**
- * Get value of attribute from variable sized attribute field.
- *
- * @max: The maximum size of this attribute. This value is dictated by
- * the maximum value from the WLP specification.
- *
- * @attr: Pointer to attribute field.
- * @value: Pointer to variable that will contain the value. The memory
- * must already have been allocated for this value.
- * @buflen: Size of buffer in which attribute field (including header)
- * can be found.
- * @returns: Amount of given bufferconsumed by parsing for this attribute.
- */
-#define wlp_vget(type_val, type_code, name, max) \
-static ssize_t wlp_get_##name(struct wlp *wlp, \
- struct wlp_attr_##name *attr, \
- type_val *value, ssize_t buflen) \
-{ \
- return wlp_vget_attribute(wlp, (type_code), &attr->hdr, \
- value, (max), buflen); \
-}
-
-wlp_get(u8, WLP_ATTR_WLP_VER, version)
-wlp_get_sparse(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
-wlp_get_sparse(struct wlp_dev_type, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
-wlp_get_sparse(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
-wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_E, uuid_e)
-wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_R, uuid_r)
-wlp_get(struct wlp_uuid, WLP_ATTR_WSSID, wssid)
-wlp_get_sparse(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
-wlp_get_sparse(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
-wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_BCAST, wss_bcast)
-wlp_get_sparse(u8, WLP_ATTR_WSS_TAG, wss_tag)
-wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_VIRT, wss_virt)
-wlp_get_sparse(struct wlp_nonce, WLP_ATTR_ENRL_NONCE, enonce)
-wlp_get_sparse(struct wlp_nonce, WLP_ATTR_REG_NONCE, rnonce)
-
-/* The buffers for the device info attributes can be found in the
- * wlp_device_info struct. These buffers contain one byte more than the
- * max allowed by the spec - this is done to be able to add the
- * terminating \0 for user display. This terminating byte is not required
- * in the actual attribute field (because it has a length field) so the
- * maximum allowed for this value is one less than its size in the
- * structure.
- */
-wlp_vget(char, WLP_ATTR_WSS_NAME, wss_name,
- FIELD_SIZEOF(struct wlp_wss, name) - 1)
-wlp_vget(char, WLP_ATTR_DEV_NAME, dev_name,
- FIELD_SIZEOF(struct wlp_device_info, name) - 1)
-wlp_vget(char, WLP_ATTR_MANUF, manufacturer,
- FIELD_SIZEOF(struct wlp_device_info, manufacturer) - 1)
-wlp_vget(char, WLP_ATTR_MODEL_NAME, model_name,
- FIELD_SIZEOF(struct wlp_device_info, model_name) - 1)
-wlp_vget(char, WLP_ATTR_MODEL_NR, model_nr,
- FIELD_SIZEOF(struct wlp_device_info, model_nr) - 1)
-wlp_vget(char, WLP_ATTR_SERIAL, serial,
- FIELD_SIZEOF(struct wlp_device_info, serial) - 1)
-
-/**
- * Retrieve WSS Name, Accept enroll, Secure status, Broadcast from WSS info
- *
- * @attr: pointer to WSS name attribute in WSS information attribute field
- * @info: structure that will be populated with data from WSS information
- * field (WSS name, Accept enroll, secure status, broadcast address)
- * @buflen: size of buffer
- *
- * Although the WSSID attribute forms part of the WSS info attribute it is
- * retrieved separately and stored in a different location.
- */
-static ssize_t wlp_get_wss_info_attrs(struct wlp *wlp,
- struct wlp_attr_hdr *attr,
- struct wlp_wss_tmp_info *info,
- ssize_t buflen)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- void *ptr = attr;
- size_t used = 0;
- ssize_t result = -EINVAL;
-
- result = wlp_get_wss_name(wlp, ptr, info->name, buflen);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSS name from "
- "WSS info in D2 message.\n");
- goto error_parse;
- }
- used += result;
-
- result = wlp_get_accept_enrl(wlp, ptr + used, &info->accept_enroll,
- buflen - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain accepting "
- "enrollment from WSS info in D2 message.\n");
- goto error_parse;
- }
- if (info->accept_enroll != 0 && info->accept_enroll != 1) {
- dev_err(dev, "WLP: invalid value for accepting "
- "enrollment in D2 message.\n");
- result = -EINVAL;
- goto error_parse;
- }
- used += result;
-
- result = wlp_get_wss_sec_status(wlp, ptr + used, &info->sec_status,
- buflen - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain secure "
- "status from WSS info in D2 message.\n");
- goto error_parse;
- }
- if (info->sec_status != 0 && info->sec_status != 1) {
- dev_err(dev, "WLP: invalid value for secure "
- "status in D2 message.\n");
- result = -EINVAL;
- goto error_parse;
- }
- used += result;
-
- result = wlp_get_wss_bcast(wlp, ptr + used, &info->bcast,
- buflen - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain broadcast "
- "address from WSS info in D2 message.\n");
- goto error_parse;
- }
- used += result;
- result = used;
-error_parse:
- return result;
-}
-
-/**
- * Create a new WSSID entry for the neighbor, allocate temporary storage
- *
- * Each neighbor can have many WSS active. We maintain a list of WSSIDs
- * advertised by neighbor. During discovery we also cache information about
- * these WSS in temporary storage.
- *
- * The temporary storage will be removed after it has been used (eg.
- * displayed to user), the wssid element will be removed from the list when
- * the neighbor is rediscovered or when it disappears.
- */
-static struct wlp_wssid_e *wlp_create_wssid_e(struct wlp *wlp,
- struct wlp_neighbor_e *neighbor)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_wssid_e *wssid_e;
-
- wssid_e = kzalloc(sizeof(*wssid_e), GFP_KERNEL);
- if (wssid_e == NULL) {
- dev_err(dev, "WLP: unable to allocate memory "
- "for WSS information.\n");
- goto error_alloc;
- }
- wssid_e->info = kzalloc(sizeof(struct wlp_wss_tmp_info), GFP_KERNEL);
- if (wssid_e->info == NULL) {
- dev_err(dev, "WLP: unable to allocate memory "
- "for temporary WSS information.\n");
- kfree(wssid_e);
- wssid_e = NULL;
- goto error_alloc;
- }
- list_add(&wssid_e->node, &neighbor->wssid);
-error_alloc:
- return wssid_e;
-}
-
-/**
- * Parse WSS information attribute
- *
- * @attr: pointer to WSS information attribute header
- * @buflen: size of buffer in which WSS information attribute appears
- * @wssid: will place wssid from WSS info attribute in this location
- * @wss_info: will place other information from WSS information attribute
- * in this location
- *
- * memory for @wssid and @wss_info must be allocated when calling this
- */
-static ssize_t wlp_get_wss_info(struct wlp *wlp, struct wlp_attr_wss_info *attr,
- size_t buflen, struct wlp_uuid *wssid,
- struct wlp_wss_tmp_info *wss_info)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- ssize_t result;
- size_t len;
- size_t used = 0;
- void *ptr;
-
- result = wlp_check_wss_info_attr_hdr(wlp, (struct wlp_attr_hdr *)attr,
- buflen);
- if (result < 0)
- goto out;
- len = result;
- used = sizeof(*attr);
- ptr = attr;
-
- result = wlp_get_wssid(wlp, ptr + used, wssid, buflen - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSSID from WSS info.\n");
- goto out;
- }
- used += result;
- result = wlp_get_wss_info_attrs(wlp, ptr + used, wss_info,
- buflen - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSS information "
- "from WSS information attributes. \n");
- goto out;
- }
- used += result;
- if (len + sizeof(*attr) != used) {
- dev_err(dev, "WLP: Amount of data parsed does not "
- "match length field. Parsed %zu, length "
- "field %zu. \n", used, len);
- result = -EINVAL;
- goto out;
- }
- result = used;
-out:
- return result;
-}
-
-/**
- * Retrieve WSS info from association frame
- *
- * @attr: pointer to WSS information attribute
- * @neighbor: ptr to neighbor being discovered, NULL if enrollment in
- * progress
- * @wss: ptr to WSS being enrolled in, NULL if discovery in progress
- * @buflen: size of buffer in which WSS information appears
- *
- * The WSS information attribute appears in the D2 association message.
- * This message is used in two ways: to discover all neighbors or to enroll
- * into a WSS activated by a neighbor. During discovery we only want to
- * store the WSS info in a cache, to be deleted right after it has been
- * used (eg. displayed to the user). During enrollment we store the WSS
- * information for the lifetime of enrollment.
- *
- * During discovery we are interested in all WSS information, during
- * enrollment we are only interested in the WSS being enrolled in. Even so,
- * when in enrollment we keep parsing the message after finding the WSS of
- * interest, this simplifies the calling routine in that it can be sure
- * that all WSS information attributes have been parsed out of the message.
- *
- * Association frame is process with nbmutex held. The list access is safe.
- */
-static ssize_t wlp_get_all_wss_info(struct wlp *wlp,
- struct wlp_attr_wss_info *attr,
- struct wlp_neighbor_e *neighbor,
- struct wlp_wss *wss, ssize_t buflen)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- size_t used = 0;
- ssize_t result = -EINVAL;
- struct wlp_attr_wss_info *cur;
- struct wlp_uuid wssid;
- struct wlp_wss_tmp_info wss_info;
- unsigned enroll; /* 0 - discovery to cache, 1 - enrollment */
- struct wlp_wssid_e *wssid_e;
- char buf[WLP_WSS_UUID_STRSIZE];
-
- if (buflen < 0)
- goto out;
-
- if (neighbor != NULL && wss == NULL)
- enroll = 0; /* discovery */
- else if (wss != NULL && neighbor == NULL)
- enroll = 1; /* enrollment */
- else
- goto out;
-
- cur = attr;
- while (buflen - used > 0) {
- memset(&wss_info, 0, sizeof(wss_info));
- cur = (void *)cur + used;
- result = wlp_get_wss_info(wlp, cur, buflen - used, &wssid,
- &wss_info);
- if (result == -ENODATA) {
- result = used;
- goto out;
- } else if (result < 0) {
- dev_err(dev, "WLP: Unable to parse WSS information "
- "from WSS information attribute. \n");
- result = -EINVAL;
- goto error_parse;
- }
- if (enroll && !memcmp(&wssid, &wss->wssid, sizeof(wssid))) {
- if (wss_info.accept_enroll != 1) {
- dev_err(dev, "WLP: Requested WSS does "
- "not accept enrollment.\n");
- result = -EINVAL;
- goto out;
- }
- memcpy(wss->name, wss_info.name, sizeof(wss->name));
- wss->bcast = wss_info.bcast;
- wss->secure_status = wss_info.sec_status;
- wss->accept_enroll = wss_info.accept_enroll;
- wss->state = WLP_WSS_STATE_PART_ENROLLED;
- wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
- dev_dbg(dev, "WLP: Found WSS %s. Enrolling.\n", buf);
- } else {
- wssid_e = wlp_create_wssid_e(wlp, neighbor);
- if (wssid_e == NULL) {
- dev_err(dev, "WLP: Cannot create new WSSID "
- "entry for neighbor %02x:%02x.\n",
- neighbor->uwb_dev->dev_addr.data[1],
- neighbor->uwb_dev->dev_addr.data[0]);
- result = -ENOMEM;
- goto out;
- }
- wssid_e->wssid = wssid;
- *wssid_e->info = wss_info;
- }
- used += result;
- }
- result = used;
-error_parse:
- if (result < 0 && !enroll) /* this was a discovery */
- wlp_remove_neighbor_tmp_info(neighbor);
-out:
- return result;
-
-}
-
-/**
- * Parse WSS information attributes into cache for discovery
- *
- * @attr: the first WSS information attribute in message
- * @neighbor: the neighbor whose cache will be populated
- * @buflen: size of the input buffer
- */
-static ssize_t wlp_get_wss_info_to_cache(struct wlp *wlp,
- struct wlp_attr_wss_info *attr,
- struct wlp_neighbor_e *neighbor,
- ssize_t buflen)
-{
- return wlp_get_all_wss_info(wlp, attr, neighbor, NULL, buflen);
-}
-
-/**
- * Parse WSS information attributes into WSS struct for enrollment
- *
- * @attr: the first WSS information attribute in message
- * @wss: the WSS that will be enrolled
- * @buflen: size of the input buffer
- */
-static ssize_t wlp_get_wss_info_to_enroll(struct wlp *wlp,
- struct wlp_attr_wss_info *attr,
- struct wlp_wss *wss, ssize_t buflen)
-{
- return wlp_get_all_wss_info(wlp, attr, NULL, wss, buflen);
-}
-
-/**
- * Construct a D1 association frame
- *
- * We use the radio control functions to determine the values of the device
- * properties. These are of variable length and the total space needed is
- * tallied first before we start constructing the message. The radio
- * control functions return strings that are terminated with \0. This
- * character should not be included in the message (there is a length field
- * accompanying it in the attribute).
- */
-static int wlp_build_assoc_d1(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb)
-{
-
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = 0;
- struct wlp_device_info *info;
- size_t used = 0;
- struct wlp_frame_assoc *_d1;
- struct sk_buff *_skb;
- void *d1_itr;
-
- if (wlp->dev_info == NULL) {
- result = __wlp_setup_device_info(wlp);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to setup device "
- "information for D1 message.\n");
- goto error;
- }
- }
- info = wlp->dev_info;
- _skb = dev_alloc_skb(sizeof(*_d1)
- + sizeof(struct wlp_attr_uuid_e)
- + sizeof(struct wlp_attr_wss_sel_mthd)
- + sizeof(struct wlp_attr_dev_name)
- + strlen(info->name)
- + sizeof(struct wlp_attr_manufacturer)
- + strlen(info->manufacturer)
- + sizeof(struct wlp_attr_model_name)
- + strlen(info->model_name)
- + sizeof(struct wlp_attr_model_nr)
- + strlen(info->model_nr)
- + sizeof(struct wlp_attr_serial)
- + strlen(info->serial)
- + sizeof(struct wlp_attr_prim_dev_type)
- + sizeof(struct wlp_attr_wlp_assc_err));
- if (_skb == NULL) {
- dev_err(dev, "WLP: Cannot allocate memory for association "
- "message.\n");
- result = -ENOMEM;
- goto error;
- }
- _d1 = (void *) _skb->data;
- _d1->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
- _d1->hdr.type = WLP_FRAME_ASSOCIATION;
- _d1->type = WLP_ASSOC_D1;
-
- wlp_set_version(&_d1->version, WLP_VERSION);
- wlp_set_msg_type(&_d1->msg_type, WLP_ASSOC_D1);
- d1_itr = _d1->attr;
- used = wlp_set_uuid_e(d1_itr, &wlp->uuid);
- used += wlp_set_wss_sel_mthd(d1_itr + used, WLP_WSS_REG_SELECT);
- used += wlp_set_dev_name(d1_itr + used, info->name,
- strlen(info->name));
- used += wlp_set_manufacturer(d1_itr + used, info->manufacturer,
- strlen(info->manufacturer));
- used += wlp_set_model_name(d1_itr + used, info->model_name,
- strlen(info->model_name));
- used += wlp_set_model_nr(d1_itr + used, info->model_nr,
- strlen(info->model_nr));
- used += wlp_set_serial(d1_itr + used, info->serial,
- strlen(info->serial));
- used += wlp_set_prim_dev_type(d1_itr + used, &info->prim_dev_type);
- used += wlp_set_wlp_assc_err(d1_itr + used, WLP_ASSOC_ERROR_NONE);
- skb_put(_skb, sizeof(*_d1) + used);
- *skb = _skb;
-error:
- return result;
-}
-
-/**
- * Construct a D2 association frame
- *
- * We use the radio control functions to determine the values of the device
- * properties. These are of variable length and the total space needed is
- * tallied first before we start constructing the message. The radio
- * control functions return strings that are terminated with \0. This
- * character should not be included in the message (there is a length field
- * accompanying it in the attribute).
- */
-static
-int wlp_build_assoc_d2(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb, struct wlp_uuid *uuid_e)
-{
-
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = 0;
- struct wlp_device_info *info;
- size_t used = 0;
- struct wlp_frame_assoc *_d2;
- struct sk_buff *_skb;
- void *d2_itr;
- size_t mem_needed;
-
- if (wlp->dev_info == NULL) {
- result = __wlp_setup_device_info(wlp);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to setup device "
- "information for D2 message.\n");
- goto error;
- }
- }
- info = wlp->dev_info;
- mem_needed = sizeof(*_d2)
- + sizeof(struct wlp_attr_uuid_e)
- + sizeof(struct wlp_attr_uuid_r)
- + sizeof(struct wlp_attr_dev_name)
- + strlen(info->name)
- + sizeof(struct wlp_attr_manufacturer)
- + strlen(info->manufacturer)
- + sizeof(struct wlp_attr_model_name)
- + strlen(info->model_name)
- + sizeof(struct wlp_attr_model_nr)
- + strlen(info->model_nr)
- + sizeof(struct wlp_attr_serial)
- + strlen(info->serial)
- + sizeof(struct wlp_attr_prim_dev_type)
- + sizeof(struct wlp_attr_wlp_assc_err);
- if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
- mem_needed += sizeof(struct wlp_attr_wss_info)
- + sizeof(struct wlp_wss_info)
- + strlen(wlp->wss.name);
- _skb = dev_alloc_skb(mem_needed);
- if (_skb == NULL) {
- dev_err(dev, "WLP: Cannot allocate memory for association "
- "message.\n");
- result = -ENOMEM;
- goto error;
- }
- _d2 = (void *) _skb->data;
- _d2->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
- _d2->hdr.type = WLP_FRAME_ASSOCIATION;
- _d2->type = WLP_ASSOC_D2;
-
- wlp_set_version(&_d2->version, WLP_VERSION);
- wlp_set_msg_type(&_d2->msg_type, WLP_ASSOC_D2);
- d2_itr = _d2->attr;
- used = wlp_set_uuid_e(d2_itr, uuid_e);
- used += wlp_set_uuid_r(d2_itr + used, &wlp->uuid);
- if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
- used += wlp_set_wss_info(d2_itr + used, &wlp->wss);
- used += wlp_set_dev_name(d2_itr + used, info->name,
- strlen(info->name));
- used += wlp_set_manufacturer(d2_itr + used, info->manufacturer,
- strlen(info->manufacturer));
- used += wlp_set_model_name(d2_itr + used, info->model_name,
- strlen(info->model_name));
- used += wlp_set_model_nr(d2_itr + used, info->model_nr,
- strlen(info->model_nr));
- used += wlp_set_serial(d2_itr + used, info->serial,
- strlen(info->serial));
- used += wlp_set_prim_dev_type(d2_itr + used, &info->prim_dev_type);
- used += wlp_set_wlp_assc_err(d2_itr + used, WLP_ASSOC_ERROR_NONE);
- skb_put(_skb, sizeof(*_d2) + used);
- *skb = _skb;
-error:
- return result;
-}
-
-/**
- * Allocate memory for and populate fields of F0 association frame
- *
- * Currently (while focusing on unsecure enrollment) we ignore the
- * nonce's that could be placed in the message. Only the error field is
- * populated by the value provided by the caller.
- */
-static
-int wlp_build_assoc_f0(struct wlp *wlp, struct sk_buff **skb,
- enum wlp_assc_error error)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = -ENOMEM;
- struct {
- struct wlp_frame_assoc f0_hdr;
- struct wlp_attr_enonce enonce;
- struct wlp_attr_rnonce rnonce;
- struct wlp_attr_wlp_assc_err assc_err;
- } *f0;
- struct sk_buff *_skb;
- struct wlp_nonce tmp;
-
- _skb = dev_alloc_skb(sizeof(*f0));
- if (_skb == NULL) {
- dev_err(dev, "WLP: Unable to allocate memory for F0 "
- "association frame. \n");
- goto error_alloc;
- }
- f0 = (void *) _skb->data;
- f0->f0_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
- f0->f0_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
- f0->f0_hdr.type = WLP_ASSOC_F0;
- wlp_set_version(&f0->f0_hdr.version, WLP_VERSION);
- wlp_set_msg_type(&f0->f0_hdr.msg_type, WLP_ASSOC_F0);
- memset(&tmp, 0, sizeof(tmp));
- wlp_set_enonce(&f0->enonce, &tmp);
- wlp_set_rnonce(&f0->rnonce, &tmp);
- wlp_set_wlp_assc_err(&f0->assc_err, error);
- skb_put(_skb, sizeof(*f0));
- *skb = _skb;
- result = 0;
-error_alloc:
- return result;
-}
-
-/**
- * Parse F0 frame
- *
- * We just retrieve the values and print it as an error to the user.
- * Calling function already knows an error occured (F0 indicates error), so
- * we just parse the content as debug for higher layers.
- */
-int wlp_parse_f0(struct wlp *wlp, struct sk_buff *skb)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_frame_assoc *f0 = (void *) skb->data;
- void *ptr = skb->data;
- size_t len = skb->len;
- size_t used;
- ssize_t result;
- struct wlp_nonce enonce, rnonce;
- enum wlp_assc_error assc_err;
- char enonce_buf[WLP_WSS_NONCE_STRSIZE];
- char rnonce_buf[WLP_WSS_NONCE_STRSIZE];
-
- used = sizeof(*f0);
- result = wlp_get_enonce(wlp, ptr + used, &enonce, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Enrollee nonce "
- "attribute from F0 message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_rnonce(wlp, ptr + used, &rnonce, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Registrar nonce "
- "attribute from F0 message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WLP Association error "
- "attribute from F0 message.\n");
- goto error_parse;
- }
- wlp_wss_nonce_print(enonce_buf, sizeof(enonce_buf), &enonce);
- wlp_wss_nonce_print(rnonce_buf, sizeof(rnonce_buf), &rnonce);
- dev_err(dev, "WLP: Received F0 error frame from neighbor. Enrollee "
- "nonce: %s, Registrar nonce: %s, WLP Association error: %s.\n",
- enonce_buf, rnonce_buf, wlp_assc_error_str(assc_err));
- result = 0;
-error_parse:
- return result;
-}
-
-/**
- * Retrieve variable device information from association message
- *
- * The device information parsed is not required in any message. This
- * routine will thus not fail if an attribute is not present.
- * The attributes are expected in a certain order, even if all are not
- * present. The "attribute type" value is used to ensure the attributes
- * are parsed in the correct order.
- *
- * If an error is encountered during parsing the function will return an
- * error code, when this happens the given device_info structure may be
- * partially filled.
- */
-static
-int wlp_get_variable_info(struct wlp *wlp, void *data,
- struct wlp_device_info *dev_info, ssize_t len)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- size_t used = 0;
- struct wlp_attr_hdr *hdr;
- ssize_t result = 0;
- unsigned last = 0;
-
- while (len - used > 0) {
- if (len - used < sizeof(*hdr)) {
- dev_err(dev, "WLP: Partial data in frame, cannot "
- "parse. \n");
- goto error_parse;
- }
- hdr = data + used;
- switch (le16_to_cpu(hdr->type)) {
- case WLP_ATTR_MANUF:
- if (last >= WLP_ATTR_MANUF) {
- dev_err(dev, "WLP: Incorrect order of "
- "attribute values in D1 msg.\n");
- goto error_parse;
- }
- result = wlp_get_manufacturer(wlp, data + used,
- dev_info->manufacturer,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to obtain "
- "Manufacturer attribute from D1 "
- "message.\n");
- goto error_parse;
- }
- last = WLP_ATTR_MANUF;
- used += result;
- break;
- case WLP_ATTR_MODEL_NAME:
- if (last >= WLP_ATTR_MODEL_NAME) {
- dev_err(dev, "WLP: Incorrect order of "
- "attribute values in D1 msg.\n");
- goto error_parse;
- }
- result = wlp_get_model_name(wlp, data + used,
- dev_info->model_name,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to obtain Model "
- "name attribute from D1 message.\n");
- goto error_parse;
- }
- last = WLP_ATTR_MODEL_NAME;
- used += result;
- break;
- case WLP_ATTR_MODEL_NR:
- if (last >= WLP_ATTR_MODEL_NR) {
- dev_err(dev, "WLP: Incorrect order of "
- "attribute values in D1 msg.\n");
- goto error_parse;
- }
- result = wlp_get_model_nr(wlp, data + used,
- dev_info->model_nr,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to obtain Model "
- "number attribute from D1 message.\n");
- goto error_parse;
- }
- last = WLP_ATTR_MODEL_NR;
- used += result;
- break;
- case WLP_ATTR_SERIAL:
- if (last >= WLP_ATTR_SERIAL) {
- dev_err(dev, "WLP: Incorrect order of "
- "attribute values in D1 msg.\n");
- goto error_parse;
- }
- result = wlp_get_serial(wlp, data + used,
- dev_info->serial, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to obtain Serial "
- "number attribute from D1 message.\n");
- goto error_parse;
- }
- last = WLP_ATTR_SERIAL;
- used += result;
- break;
- case WLP_ATTR_PRI_DEV_TYPE:
- if (last >= WLP_ATTR_PRI_DEV_TYPE) {
- dev_err(dev, "WLP: Incorrect order of "
- "attribute values in D1 msg.\n");
- goto error_parse;
- }
- result = wlp_get_prim_dev_type(wlp, data + used,
- &dev_info->prim_dev_type,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to obtain Primary "
- "device type attribute from D1 "
- "message.\n");
- goto error_parse;
- }
- dev_info->prim_dev_type.category =
- le16_to_cpu(dev_info->prim_dev_type.category);
- dev_info->prim_dev_type.subID =
- le16_to_cpu(dev_info->prim_dev_type.subID);
- last = WLP_ATTR_PRI_DEV_TYPE;
- used += result;
- break;
- default:
- /* This is not variable device information. */
- goto out;
- break;
- }
- }
-out:
- return used;
-error_parse:
- return -EINVAL;
-}
-
-/**
- * Parse incoming D1 frame, populate attribute values
- *
- * Caller provides pointers to memory already allocated for attributes
- * expected in the D1 frame. These variables will be populated.
- */
-static
-int wlp_parse_d1_frame(struct wlp *wlp, struct sk_buff *skb,
- struct wlp_uuid *uuid_e,
- enum wlp_wss_sel_mthd *sel_mthd,
- struct wlp_device_info *dev_info,
- enum wlp_assc_error *assc_err)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_frame_assoc *d1 = (void *) skb->data;
- void *ptr = skb->data;
- size_t len = skb->len;
- size_t used;
- ssize_t result;
-
- used = sizeof(*d1);
- result = wlp_get_uuid_e(wlp, ptr + used, uuid_e, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain UUID-E attribute from D1 "
- "message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_wss_sel_mthd(wlp, ptr + used, sel_mthd, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSS selection method "
- "from D1 message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_dev_name(wlp, ptr + used, dev_info->name,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Device Name from D1 "
- "message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_variable_info(wlp, ptr + used, dev_info, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Device Information from "
- "D1 message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_wlp_assc_err(wlp, ptr + used, assc_err, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WLP Association Error "
- "Information from D1 message.\n");
- goto error_parse;
- }
- result = 0;
-error_parse:
- return result;
-}
-/**
- * Handle incoming D1 frame
- *
- * The frame has already been verified to contain an Association header with
- * the correct version number. Parse the incoming frame, construct and send
- * a D2 frame in response.
- *
- * It is not clear what to do with most fields in the incoming D1 frame. We
- * retrieve and discard the information here for now.
- */
-void wlp_handle_d1_frame(struct work_struct *ws)
-{
- struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
- struct wlp_assoc_frame_ctx,
- ws);
- struct wlp *wlp = frame_ctx->wlp;
- struct wlp_wss *wss = &wlp->wss;
- struct sk_buff *skb = frame_ctx->skb;
- struct uwb_dev_addr *src = &frame_ctx->src;
- int result;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_uuid uuid_e;
- enum wlp_wss_sel_mthd sel_mthd = 0;
- struct wlp_device_info dev_info;
- enum wlp_assc_error assc_err;
- struct sk_buff *resp = NULL;
-
- /* Parse D1 frame */
- mutex_lock(&wss->mutex);
- mutex_lock(&wlp->mutex); /* to access wlp->uuid */
- memset(&dev_info, 0, sizeof(dev_info));
- result = wlp_parse_d1_frame(wlp, skb, &uuid_e, &sel_mthd, &dev_info,
- &assc_err);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to parse incoming D1 frame.\n");
- kfree_skb(skb);
- goto out;
- }
-
- kfree_skb(skb);
- if (!wlp_uuid_is_set(&wlp->uuid)) {
- dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
- "proceed. Respong to D1 message with error F0.\n");
- result = wlp_build_assoc_f0(wlp, &resp,
- WLP_ASSOC_ERROR_NOT_READY);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to construct F0 message.\n");
- goto out;
- }
- } else {
- /* Construct D2 frame */
- result = wlp_build_assoc_d2(wlp, wss, &resp, &uuid_e);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to construct D2 message.\n");
- goto out;
- }
- }
- /* Send D2 frame */
- BUG_ON(wlp->xmit_frame == NULL);
- result = wlp->xmit_frame(wlp, resp, src);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to transmit D2 association "
- "message: %d\n", result);
- if (result == -ENXIO)
- dev_err(dev, "WLP: Is network interface up? \n");
- /* We could try again ... */
- dev_kfree_skb_any(resp); /* we need to free if tx fails */
- }
-out:
- kfree(frame_ctx);
- mutex_unlock(&wlp->mutex);
- mutex_unlock(&wss->mutex);
-}
-
-/**
- * Parse incoming D2 frame, create and populate temporary cache
- *
- * @skb: socket buffer in which D2 frame can be found
- * @neighbor: the neighbor that sent the D2 frame
- *
- * Will allocate memory for temporary storage of information learned during
- * discovery.
- */
-int wlp_parse_d2_frame_to_cache(struct wlp *wlp, struct sk_buff *skb,
- struct wlp_neighbor_e *neighbor)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_frame_assoc *d2 = (void *) skb->data;
- void *ptr = skb->data;
- size_t len = skb->len;
- size_t used;
- ssize_t result;
- struct wlp_uuid uuid_e;
- struct wlp_device_info *nb_info;
- enum wlp_assc_error assc_err;
-
- used = sizeof(*d2);
- result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
- "message.\n");
- goto error_parse;
- }
- if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
- dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
- "local UUID sent in D1. \n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_uuid_r(wlp, ptr + used, &neighbor->uuid, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
- "message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_wss_info_to_cache(wlp, ptr + used, neighbor,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSS information "
- "from D2 message.\n");
- goto error_parse;
- }
- used += result;
- neighbor->info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
- if (neighbor->info == NULL) {
- dev_err(dev, "WLP: cannot allocate memory to store device "
- "info.\n");
- result = -ENOMEM;
- goto error_parse;
- }
- nb_info = neighbor->info;
- result = wlp_get_dev_name(wlp, ptr + used, nb_info->name,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Device Name from D2 "
- "message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_variable_info(wlp, ptr + used, nb_info, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Device Information from "
- "D2 message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WLP Association Error "
- "Information from D2 message.\n");
- goto error_parse;
- }
- if (assc_err != WLP_ASSOC_ERROR_NONE) {
- dev_err(dev, "WLP: neighbor device returned association "
- "error %d\n", assc_err);
- result = -EINVAL;
- goto error_parse;
- }
- result = 0;
-error_parse:
- if (result < 0)
- wlp_remove_neighbor_tmp_info(neighbor);
- return result;
-}
-
-/**
- * Parse incoming D2 frame, populate attribute values of WSS bein enrolled in
- *
- * @wss: our WSS that will be enrolled
- * @skb: socket buffer in which D2 frame can be found
- * @neighbor: the neighbor that sent the D2 frame
- * @wssid: the wssid of the WSS in which we want to enroll
- *
- * Forms part of enrollment sequence. We are trying to enroll in WSS with
- * @wssid by using @neighbor as registrar. A D1 message was sent to
- * @neighbor and now we need to parse the D2 response. The neighbor's
- * response is searched for the requested WSS and if found (and it accepts
- * enrollment), we store the information.
- */
-int wlp_parse_d2_frame_to_enroll(struct wlp_wss *wss, struct sk_buff *skb,
- struct wlp_neighbor_e *neighbor,
- struct wlp_uuid *wssid)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- void *ptr = skb->data;
- size_t len = skb->len;
- size_t used;
- ssize_t result;
- struct wlp_uuid uuid_e;
- struct wlp_uuid uuid_r;
- struct wlp_device_info nb_info;
- enum wlp_assc_error assc_err;
- char uuid_bufA[WLP_WSS_UUID_STRSIZE];
- char uuid_bufB[WLP_WSS_UUID_STRSIZE];
-
- used = sizeof(struct wlp_frame_assoc);
- result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
- "message.\n");
- goto error_parse;
- }
- if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
- dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
- "local UUID sent in D1. \n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_uuid_r(wlp, ptr + used, &uuid_r, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
- "message.\n");
- goto error_parse;
- }
- if (memcmp(&uuid_r, &neighbor->uuid, sizeof(uuid_r))) {
- wlp_wss_uuid_print(uuid_bufA, sizeof(uuid_bufA),
- &neighbor->uuid);
- wlp_wss_uuid_print(uuid_bufB, sizeof(uuid_bufB), &uuid_r);
- dev_err(dev, "WLP: UUID of neighbor does not match UUID "
- "learned during discovery. Originally discovered: %s, "
- "now from D2 message: %s\n", uuid_bufA, uuid_bufB);
- result = -EINVAL;
- goto error_parse;
- }
- used += result;
- wss->wssid = *wssid;
- result = wlp_get_wss_info_to_enroll(wlp, ptr + used, wss, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSS information "
- "from D2 message.\n");
- goto error_parse;
- }
- if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
- dev_err(dev, "WLP: D2 message did not contain information "
- "for successful enrollment. \n");
- result = -EINVAL;
- goto error_parse;
- }
- used += result;
- /* Place device information on stack to continue parsing of message */
- result = wlp_get_dev_name(wlp, ptr + used, nb_info.name,
- len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Device Name from D2 "
- "message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_variable_info(wlp, ptr + used, &nb_info, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain Device Information from "
- "D2 message.\n");
- goto error_parse;
- }
- used += result;
- result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WLP Association Error "
- "Information from D2 message.\n");
- goto error_parse;
- }
- if (assc_err != WLP_ASSOC_ERROR_NONE) {
- dev_err(dev, "WLP: neighbor device returned association "
- "error %d\n", assc_err);
- if (wss->state == WLP_WSS_STATE_PART_ENROLLED) {
- dev_err(dev, "WLP: Enrolled in WSS (should not "
- "happen according to spec). Undoing. \n");
- wlp_wss_reset(wss);
- }
- result = -EINVAL;
- goto error_parse;
- }
- result = 0;
-error_parse:
- return result;
-}
-
-/**
- * Parse C3/C4 frame into provided variables
- *
- * @wssid: will point to copy of wssid retrieved from C3/C4 frame
- * @tag: will point to copy of tag retrieved from C3/C4 frame
- * @virt_addr: will point to copy of virtual address retrieved from C3/C4
- * frame.
- *
- * Calling function has to allocate memory for these values.
- *
- * skb contains a valid C3/C4 frame, return the individual fields of this
- * frame in the provided variables.
- */
-int wlp_parse_c3c4_frame(struct wlp *wlp, struct sk_buff *skb,
- struct wlp_uuid *wssid, u8 *tag,
- struct uwb_mac_addr *virt_addr)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result;
- void *ptr = skb->data;
- size_t len = skb->len;
- size_t used;
- struct wlp_frame_assoc *assoc = ptr;
-
- used = sizeof(*assoc);
- result = wlp_get_wssid(wlp, ptr + used, wssid, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSSID attribute from "
- "%s message.\n", wlp_assoc_frame_str(assoc->type));
- goto error_parse;
- }
- used += result;
- result = wlp_get_wss_tag(wlp, ptr + used, tag, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSS tag attribute from "
- "%s message.\n", wlp_assoc_frame_str(assoc->type));
- goto error_parse;
- }
- used += result;
- result = wlp_get_wss_virt(wlp, ptr + used, virt_addr, len - used);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSS virtual address "
- "attribute from %s message.\n",
- wlp_assoc_frame_str(assoc->type));
- goto error_parse;
- }
-error_parse:
- return result;
-}
-
-/**
- * Allocate memory for and populate fields of C1 or C2 association frame
- *
- * The C1 and C2 association frames appear identical - except for the type.
- */
-static
-int wlp_build_assoc_c1c2(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb, enum wlp_assoc_type type)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = -ENOMEM;
- struct {
- struct wlp_frame_assoc c_hdr;
- struct wlp_attr_wssid wssid;
- } *c;
- struct sk_buff *_skb;
-
- _skb = dev_alloc_skb(sizeof(*c));
- if (_skb == NULL) {
- dev_err(dev, "WLP: Unable to allocate memory for C1/C2 "
- "association frame. \n");
- goto error_alloc;
- }
- c = (void *) _skb->data;
- c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
- c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
- c->c_hdr.type = type;
- wlp_set_version(&c->c_hdr.version, WLP_VERSION);
- wlp_set_msg_type(&c->c_hdr.msg_type, type);
- wlp_set_wssid(&c->wssid, &wss->wssid);
- skb_put(_skb, sizeof(*c));
- *skb = _skb;
- result = 0;
-error_alloc:
- return result;
-}
-
-
-static
-int wlp_build_assoc_c1(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb)
-{
- return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C1);
-}
-
-static
-int wlp_build_assoc_c2(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb)
-{
- return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C2);
-}
-
-
-/**
- * Allocate memory for and populate fields of C3 or C4 association frame
- *
- * The C3 and C4 association frames appear identical - except for the type.
- */
-static
-int wlp_build_assoc_c3c4(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb, enum wlp_assoc_type type)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = -ENOMEM;
- struct {
- struct wlp_frame_assoc c_hdr;
- struct wlp_attr_wssid wssid;
- struct wlp_attr_wss_tag wss_tag;
- struct wlp_attr_wss_virt wss_virt;
- } *c;
- struct sk_buff *_skb;
-
- _skb = dev_alloc_skb(sizeof(*c));
- if (_skb == NULL) {
- dev_err(dev, "WLP: Unable to allocate memory for C3/C4 "
- "association frame. \n");
- goto error_alloc;
- }
- c = (void *) _skb->data;
- c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
- c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
- c->c_hdr.type = type;
- wlp_set_version(&c->c_hdr.version, WLP_VERSION);
- wlp_set_msg_type(&c->c_hdr.msg_type, type);
- wlp_set_wssid(&c->wssid, &wss->wssid);
- wlp_set_wss_tag(&c->wss_tag, wss->tag);
- wlp_set_wss_virt(&c->wss_virt, &wss->virtual_addr);
- skb_put(_skb, sizeof(*c));
- *skb = _skb;
- result = 0;
-error_alloc:
- return result;
-}
-
-static
-int wlp_build_assoc_c3(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb)
-{
- return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C3);
-}
-
-static
-int wlp_build_assoc_c4(struct wlp *wlp, struct wlp_wss *wss,
- struct sk_buff **skb)
-{
- return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C4);
-}
-
-
-#define wlp_send_assoc(type, id) \
-static int wlp_send_assoc_##type(struct wlp *wlp, struct wlp_wss *wss, \
- struct uwb_dev_addr *dev_addr) \
-{ \
- struct device *dev = &wlp->rc->uwb_dev.dev; \
- int result; \
- struct sk_buff *skb = NULL; \
- \
- /* Build the frame */ \
- result = wlp_build_assoc_##type(wlp, wss, &skb); \
- if (result < 0) { \
- dev_err(dev, "WLP: Unable to construct %s association " \
- "frame: %d\n", wlp_assoc_frame_str(id), result);\
- goto error_build_assoc; \
- } \
- /* Send the frame */ \
- BUG_ON(wlp->xmit_frame == NULL); \
- result = wlp->xmit_frame(wlp, skb, dev_addr); \
- if (result < 0) { \
- dev_err(dev, "WLP: Unable to transmit %s association " \
- "message: %d\n", wlp_assoc_frame_str(id), \
- result); \
- if (result == -ENXIO) \
- dev_err(dev, "WLP: Is network interface " \
- "up? \n"); \
- goto error_xmit; \
- } \
- return 0; \
-error_xmit: \
- /* We could try again ... */ \
- dev_kfree_skb_any(skb);/*we need to free if tx fails*/ \
-error_build_assoc: \
- return result; \
-}
-
-wlp_send_assoc(d1, WLP_ASSOC_D1)
-wlp_send_assoc(c1, WLP_ASSOC_C1)
-wlp_send_assoc(c3, WLP_ASSOC_C3)
-
-int wlp_send_assoc_frame(struct wlp *wlp, struct wlp_wss *wss,
- struct uwb_dev_addr *dev_addr,
- enum wlp_assoc_type type)
-{
- int result = 0;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- switch (type) {
- case WLP_ASSOC_D1:
- result = wlp_send_assoc_d1(wlp, wss, dev_addr);
- break;
- case WLP_ASSOC_C1:
- result = wlp_send_assoc_c1(wlp, wss, dev_addr);
- break;
- case WLP_ASSOC_C3:
- result = wlp_send_assoc_c3(wlp, wss, dev_addr);
- break;
- default:
- dev_err(dev, "WLP: Received request to send unknown "
- "association message.\n");
- result = -EINVAL;
- break;
- }
- return result;
-}
-
-/**
- * Handle incoming C1 frame
- *
- * The frame has already been verified to contain an Association header with
- * the correct version number. Parse the incoming frame, construct and send
- * a C2 frame in response.
- */
-void wlp_handle_c1_frame(struct work_struct *ws)
-{
- struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
- struct wlp_assoc_frame_ctx,
- ws);
- struct wlp *wlp = frame_ctx->wlp;
- struct wlp_wss *wss = &wlp->wss;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_frame_assoc *c1 = (void *) frame_ctx->skb->data;
- unsigned int len = frame_ctx->skb->len;
- struct uwb_dev_addr *src = &frame_ctx->src;
- int result;
- struct wlp_uuid wssid;
- struct sk_buff *resp = NULL;
-
- /* Parse C1 frame */
- mutex_lock(&wss->mutex);
- result = wlp_get_wssid(wlp, (void *)c1 + sizeof(*c1), &wssid,
- len - sizeof(*c1));
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSSID from C1 frame.\n");
- goto out;
- }
- if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
- && wss->state == WLP_WSS_STATE_ACTIVE) {
- /* Construct C2 frame */
- result = wlp_build_assoc_c2(wlp, wss, &resp);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to construct C2 message.\n");
- goto out;
- }
- } else {
- /* Construct F0 frame */
- result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to construct F0 message.\n");
- goto out;
- }
- }
- /* Send C2 frame */
- BUG_ON(wlp->xmit_frame == NULL);
- result = wlp->xmit_frame(wlp, resp, src);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to transmit response association "
- "message: %d\n", result);
- if (result == -ENXIO)
- dev_err(dev, "WLP: Is network interface up? \n");
- /* We could try again ... */
- dev_kfree_skb_any(resp); /* we need to free if tx fails */
- }
-out:
- kfree_skb(frame_ctx->skb);
- kfree(frame_ctx);
- mutex_unlock(&wss->mutex);
-}
-
-/**
- * Handle incoming C3 frame
- *
- * The frame has already been verified to contain an Association header with
- * the correct version number. Parse the incoming frame, construct and send
- * a C4 frame in response. If the C3 frame identifies a WSS that is locally
- * active then we connect to this neighbor (add it to our EDA cache).
- */
-void wlp_handle_c3_frame(struct work_struct *ws)
-{
- struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
- struct wlp_assoc_frame_ctx,
- ws);
- struct wlp *wlp = frame_ctx->wlp;
- struct wlp_wss *wss = &wlp->wss;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct sk_buff *skb = frame_ctx->skb;
- struct uwb_dev_addr *src = &frame_ctx->src;
- int result;
- struct sk_buff *resp = NULL;
- struct wlp_uuid wssid;
- u8 tag;
- struct uwb_mac_addr virt_addr;
-
- /* Parse C3 frame */
- mutex_lock(&wss->mutex);
- result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain values from C3 frame.\n");
- goto out;
- }
- if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
- && wss->state >= WLP_WSS_STATE_ACTIVE) {
- result = wlp_eda_update_node(&wlp->eda, src, wss,
- (void *) virt_addr.data, tag,
- WLP_WSS_CONNECTED);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to update EDA cache "
- "with new connected neighbor information.\n");
- result = wlp_build_assoc_f0(wlp, &resp,
- WLP_ASSOC_ERROR_INT);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to construct F0 "
- "message.\n");
- goto out;
- }
- } else {
- wss->state = WLP_WSS_STATE_CONNECTED;
- /* Construct C4 frame */
- result = wlp_build_assoc_c4(wlp, wss, &resp);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to construct C4 "
- "message.\n");
- goto out;
- }
- }
- } else {
- /* Construct F0 frame */
- result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to construct F0 message.\n");
- goto out;
- }
- }
- /* Send C4 frame */
- BUG_ON(wlp->xmit_frame == NULL);
- result = wlp->xmit_frame(wlp, resp, src);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to transmit response association "
- "message: %d\n", result);
- if (result == -ENXIO)
- dev_err(dev, "WLP: Is network interface up? \n");
- /* We could try again ... */
- dev_kfree_skb_any(resp); /* we need to free if tx fails */
- }
-out:
- kfree_skb(frame_ctx->skb);
- kfree(frame_ctx);
- mutex_unlock(&wss->mutex);
-}
-
-
diff --git a/drivers/uwb/wlp/sysfs.c b/drivers/uwb/wlp/sysfs.c
deleted file mode 100644
index 6627c94cc854..000000000000
--- a/drivers/uwb/wlp/sysfs.c
+++ /dev/null
@@ -1,708 +0,0 @@
-/*
- * WiMedia Logical Link Control Protocol (WLP)
- * sysfs functions
- *
- * Copyright (C) 2007 Intel Corporation
- * Reinette Chatre <reinette.chatre@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: Docs
- *
- */
-#include <linux/wlp.h>
-
-#include "wlp-internal.h"
-
-static
-size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize,
- struct wlp_wssid_e *wssid_e)
-{
- size_t used = 0;
- used += scnprintf(buf, bufsize, " WSS: ");
- used += wlp_wss_uuid_print(buf + used, bufsize - used,
- &wssid_e->wssid);
-
- if (wssid_e->info != NULL) {
- used += scnprintf(buf + used, bufsize - used, " ");
- used += uwb_mac_addr_print(buf + used, bufsize - used,
- &wssid_e->info->bcast);
- used += scnprintf(buf + used, bufsize - used, " %u %u %s\n",
- wssid_e->info->accept_enroll,
- wssid_e->info->sec_status,
- wssid_e->info->name);
- }
- return used;
-}
-
-/**
- * Print out information learned from neighbor discovery
- *
- * Some fields being printed may not be included in the device discovery
- * information (it is not mandatory). We are thus careful how the
- * information is printed to ensure it is clear to the user what field is
- * being referenced.
- * The information being printed is for one time use - temporary storage is
- * cleaned after it is printed.
- *
- * Ideally sysfs output should be on one line. The information printed here
- * contain a few strings so it will be hard to parse if they are all
- * printed on the same line - without agreeing on a standard field
- * separator.
- */
-static
-ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf,
- size_t bufsize)
-{
- size_t used = 0;
- struct wlp_neighbor_e *neighb;
- struct wlp_wssid_e *wssid_e;
-
- mutex_lock(&wlp->nbmutex);
- used = scnprintf(buf, bufsize, "#Neighbor information\n"
- "#uuid dev_addr\n"
- "# Device Name:\n# Model Name:\n# Manufacturer:\n"
- "# Model Nr:\n# Serial:\n"
- "# Pri Dev type: CategoryID OUI OUISubdiv "
- "SubcategoryID\n"
- "# WSS: WSSID WSS_name accept_enroll sec_status "
- "bcast\n"
- "# WSS: WSSID WSS_name accept_enroll sec_status "
- "bcast\n\n");
- list_for_each_entry(neighb, &wlp->neighbors, node) {
- if (bufsize - used <= 0)
- goto out;
- used += wlp_wss_uuid_print(buf + used, bufsize - used,
- &neighb->uuid);
- buf[used++] = ' ';
- used += uwb_dev_addr_print(buf + used, bufsize - used,
- &neighb->uwb_dev->dev_addr);
- if (neighb->info != NULL)
- used += scnprintf(buf + used, bufsize - used,
- "\n Device Name: %s\n"
- " Model Name: %s\n"
- " Manufacturer:%s \n"
- " Model Nr: %s\n"
- " Serial: %s\n"
- " Pri Dev type: "
- "%u %02x:%02x:%02x %u %u\n",
- neighb->info->name,
- neighb->info->model_name,
- neighb->info->manufacturer,
- neighb->info->model_nr,
- neighb->info->serial,
- neighb->info->prim_dev_type.category,
- neighb->info->prim_dev_type.OUI[0],
- neighb->info->prim_dev_type.OUI[1],
- neighb->info->prim_dev_type.OUI[2],
- neighb->info->prim_dev_type.OUIsubdiv,
- neighb->info->prim_dev_type.subID);
- list_for_each_entry(wssid_e, &neighb->wssid, node) {
- used += wlp_wss_wssid_e_print(buf + used,
- bufsize - used,
- wssid_e);
- }
- buf[used++] = '\n';
- wlp_remove_neighbor_tmp_info(neighb);
- }
-
-
-out:
- mutex_unlock(&wlp->nbmutex);
- return used;
-}
-
-
-/**
- * Show properties of all WSS in neighborhood.
- *
- * Will trigger a complete discovery of WSS activated by this device and
- * its neighbors.
- */
-ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf)
-{
- wlp_discover(wlp);
- return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE);
-}
-EXPORT_SYMBOL_GPL(wlp_neighborhood_show);
-
-static
-ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf,
- size_t bufsize)
-{
- ssize_t result;
-
- result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid);
- result += scnprintf(buf + result, bufsize - result, " ");
- result += uwb_mac_addr_print(buf + result, bufsize - result,
- &wss->bcast);
- result += scnprintf(buf + result, bufsize - result,
- " 0x%02x %u ", wss->hash, wss->secure_status);
- result += wlp_wss_key_print(buf + result, bufsize - result,
- wss->master_key);
- result += scnprintf(buf + result, bufsize - result, " 0x%02x ",
- wss->tag);
- result += uwb_mac_addr_print(buf + result, bufsize - result,
- &wss->virtual_addr);
- result += scnprintf(buf + result, bufsize - result, " %s", wss->name);
- result += scnprintf(buf + result, bufsize - result,
- "\n\n#WSSID\n#WSS broadcast address\n"
- "#WSS hash\n#WSS secure status\n"
- "#WSS master key\n#WSS local tag\n"
- "#WSS local virtual EUI-48\n#WSS name\n");
- return result;
-}
-
-/**
- * Show which WSS is activated.
- */
-ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf)
-{
- int result = 0;
-
- if (mutex_lock_interruptible(&wss->mutex))
- goto out;
- if (wss->state >= WLP_WSS_STATE_ACTIVE)
- result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
- else
- result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n");
- result += scnprintf(buf + result, PAGE_SIZE - result,
- "\n\n"
- "# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT "
- "NAME #create new WSS\n"
- "# echo WSSID [DEV ADDR] #enroll in and activate "
- "existing WSS, can request registrar\n"
- "#\n"
- "# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n"
- "# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n"
- "# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n"
- "# NAME is the text string identifying the WSS\n"
- "# DEV ADDR is the device address of neighbor "
- "that should be registrar. Eg. 32:AB\n");
-
- mutex_unlock(&wss->mutex);
-out:
- return result;
-
-}
-EXPORT_SYMBOL_GPL(wlp_wss_activate_show);
-
-/**
- * Create/activate a new WSS or enroll/activate in neighboring WSS
- *
- * The user can provide the WSSID of a WSS in which it wants to enroll.
- * Only the WSSID is necessary if the WSS have been discovered before. If
- * the WSS has not been discovered before, or the user wants to use a
- * particular neighbor as its registrar, then the user can also provide a
- * device address or the neighbor that will be used as registrar.
- *
- * A new WSS is created when the user provides a WSSID, secure status, and
- * WSS name.
- */
-ssize_t wlp_wss_activate_store(struct wlp_wss *wss,
- const char *buf, size_t size)
-{
- ssize_t result = -EINVAL;
- struct wlp_uuid wssid;
- struct uwb_dev_addr dev;
- struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
- char name[65];
- unsigned sec_status, accept;
- memset(name, 0, sizeof(name));
- result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx:%02hhx",
- &wssid.data[0] , &wssid.data[1],
- &wssid.data[2] , &wssid.data[3],
- &wssid.data[4] , &wssid.data[5],
- &wssid.data[6] , &wssid.data[7],
- &wssid.data[8] , &wssid.data[9],
- &wssid.data[10], &wssid.data[11],
- &wssid.data[12], &wssid.data[13],
- &wssid.data[14], &wssid.data[15],
- &dev.data[1], &dev.data[0]);
- if (result == 16 || result == 17) {
- result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%u %u %64c",
- &wssid.data[0] , &wssid.data[1],
- &wssid.data[2] , &wssid.data[3],
- &wssid.data[4] , &wssid.data[5],
- &wssid.data[6] , &wssid.data[7],
- &wssid.data[8] , &wssid.data[9],
- &wssid.data[10], &wssid.data[11],
- &wssid.data[12], &wssid.data[13],
- &wssid.data[14], &wssid.data[15],
- &sec_status, &accept, name);
- if (result == 16)
- result = wlp_wss_enroll_activate(wss, &wssid, &bcast);
- else if (result == 19) {
- sec_status = sec_status == 0 ? 0 : 1;
- accept = accept == 0 ? 0 : 1;
- /* We read name using %c, so the newline needs to be
- * removed */
- if (strlen(name) != sizeof(name) - 1)
- name[strlen(name) - 1] = '\0';
- result = wlp_wss_create_activate(wss, &wssid, name,
- sec_status, accept);
- } else
- result = -EINVAL;
- } else if (result == 18)
- result = wlp_wss_enroll_activate(wss, &wssid, &dev);
- else
- result = -EINVAL;
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(wlp_wss_activate_store);
-
-/**
- * Show the UUID of this host
- */
-ssize_t wlp_uuid_show(struct wlp *wlp, char *buf)
-{
- ssize_t result = 0;
-
- mutex_lock(&wlp->mutex);
- result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid);
- buf[result++] = '\n';
- mutex_unlock(&wlp->mutex);
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_uuid_show);
-
-/**
- * Store a new UUID for this host
- *
- * According to the spec this should be encoded as an octet string in the
- * order the octets are shown in string representation in RFC 4122 (WLP
- * 0.99 [Table 6])
- *
- * We do not check value provided by user.
- */
-ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size)
-{
- ssize_t result;
- struct wlp_uuid uuid;
-
- mutex_lock(&wlp->mutex);
- result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx "
- "%02hhx %02hhx %02hhx %02hhx ",
- &uuid.data[0] , &uuid.data[1],
- &uuid.data[2] , &uuid.data[3],
- &uuid.data[4] , &uuid.data[5],
- &uuid.data[6] , &uuid.data[7],
- &uuid.data[8] , &uuid.data[9],
- &uuid.data[10], &uuid.data[11],
- &uuid.data[12], &uuid.data[13],
- &uuid.data[14], &uuid.data[15]);
- if (result != 16) {
- result = -EINVAL;
- goto error;
- }
- wlp->uuid = uuid;
-error:
- mutex_unlock(&wlp->mutex);
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(wlp_uuid_store);
-
-/**
- * Show contents of members of device information structure
- */
-#define wlp_dev_info_show(type) \
-ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \
-{ \
- ssize_t result = 0; \
- mutex_lock(&wlp->mutex); \
- if (wlp->dev_info == NULL) { \
- result = __wlp_setup_device_info(wlp); \
- if (result < 0) \
- goto out; \
- } \
- result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\
-out: \
- mutex_unlock(&wlp->mutex); \
- return result; \
-} \
-EXPORT_SYMBOL_GPL(wlp_dev_##type##_show);
-
-wlp_dev_info_show(name)
-wlp_dev_info_show(model_name)
-wlp_dev_info_show(model_nr)
-wlp_dev_info_show(manufacturer)
-wlp_dev_info_show(serial)
-
-/**
- * Store contents of members of device information structure
- */
-#define wlp_dev_info_store(type, len) \
-ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\
-{ \
- ssize_t result; \
- char format[10]; \
- mutex_lock(&wlp->mutex); \
- if (wlp->dev_info == NULL) { \
- result = __wlp_alloc_device_info(wlp); \
- if (result < 0) \
- goto out; \
- } \
- memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \
- sprintf(format, "%%%uc", len); \
- result = sscanf(buf, format, wlp->dev_info->type); \
-out: \
- mutex_unlock(&wlp->mutex); \
- return result < 0 ? result : size; \
-} \
-EXPORT_SYMBOL_GPL(wlp_dev_##type##_store);
-
-wlp_dev_info_store(name, 32)
-wlp_dev_info_store(manufacturer, 64)
-wlp_dev_info_store(model_name, 32)
-wlp_dev_info_store(model_nr, 32)
-wlp_dev_info_store(serial, 32)
-
-static
-const char *__wlp_dev_category[] = {
- [WLP_DEV_CAT_COMPUTER] = "Computer",
- [WLP_DEV_CAT_INPUT] = "Input device",
- [WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or "
- "Copier",
- [WLP_DEV_CAT_CAMERA] = "Camera",
- [WLP_DEV_CAT_STORAGE] = "Storage Network",
- [WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure",
- [WLP_DEV_CAT_DISPLAY] = "Display",
- [WLP_DEV_CAT_MULTIM] = "Multimedia device",
- [WLP_DEV_CAT_GAMING] = "Gaming device",
- [WLP_DEV_CAT_TELEPHONE] = "Telephone",
- [WLP_DEV_CAT_OTHER] = "Other",
-};
-
-static
-const char *wlp_dev_category_str(unsigned cat)
-{
- if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
- || cat == WLP_DEV_CAT_OTHER)
- return __wlp_dev_category[cat];
- return "unknown category";
-}
-
-ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf)
-{
- ssize_t result = 0;
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_setup_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = scnprintf(buf, PAGE_SIZE, "%s\n",
- wlp_dev_category_str(wlp->dev_info->prim_dev_type.category));
-out:
- mutex_unlock(&wlp->mutex);
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show);
-
-ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf,
- size_t size)
-{
- ssize_t result;
- u16 cat;
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_alloc_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = sscanf(buf, "%hu", &cat);
- if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
- || cat == WLP_DEV_CAT_OTHER)
- wlp->dev_info->prim_dev_type.category = cat;
- else
- result = -EINVAL;
-out:
- mutex_unlock(&wlp->mutex);
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store);
-
-ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf)
-{
- ssize_t result = 0;
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_setup_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n",
- wlp->dev_info->prim_dev_type.OUI[0],
- wlp->dev_info->prim_dev_type.OUI[1],
- wlp->dev_info->prim_dev_type.OUI[2]);
-out:
- mutex_unlock(&wlp->mutex);
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show);
-
-ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size)
-{
- ssize_t result;
- u8 OUI[3];
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_alloc_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = sscanf(buf, "%hhx:%hhx:%hhx",
- &OUI[0], &OUI[1], &OUI[2]);
- if (result != 3) {
- result = -EINVAL;
- goto out;
- } else
- memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI));
-out:
- mutex_unlock(&wlp->mutex);
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store);
-
-
-ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf)
-{
- ssize_t result = 0;
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_setup_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = scnprintf(buf, PAGE_SIZE, "%u\n",
- wlp->dev_info->prim_dev_type.OUIsubdiv);
-out:
- mutex_unlock(&wlp->mutex);
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show);
-
-ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf,
- size_t size)
-{
- ssize_t result;
- unsigned sub;
- u8 max_sub = ~0;
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_alloc_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = sscanf(buf, "%u", &sub);
- if (sub <= max_sub)
- wlp->dev_info->prim_dev_type.OUIsubdiv = sub;
- else
- result = -EINVAL;
-out:
- mutex_unlock(&wlp->mutex);
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store);
-
-ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf)
-{
- ssize_t result = 0;
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_setup_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = scnprintf(buf, PAGE_SIZE, "%u\n",
- wlp->dev_info->prim_dev_type.subID);
-out:
- mutex_unlock(&wlp->mutex);
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show);
-
-ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf,
- size_t size)
-{
- ssize_t result;
- unsigned sub;
- __le16 max_sub = ~0;
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info == NULL) {
- result = __wlp_alloc_device_info(wlp);
- if (result < 0)
- goto out;
- }
- result = sscanf(buf, "%u", &sub);
- if (sub <= max_sub)
- wlp->dev_info->prim_dev_type.subID = sub;
- else
- result = -EINVAL;
-out:
- mutex_unlock(&wlp->mutex);
- return result < 0 ? result : size;
-}
-EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store);
-
-/**
- * Subsystem implementation for interaction with individual WSS via sysfs
- *
- * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt
- */
-
-#define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj)
-#define attr_to_wlp_wss_attr(_attr) \
- container_of(_attr, struct wlp_wss_attribute, attr)
-
-/**
- * Sysfs subsystem: forward read calls
- *
- * Sysfs operation for forwarding read call to the show method of the
- * attribute owner
- */
-static
-ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
- struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
- ssize_t ret = -EIO;
-
- if (wss_attr->show)
- ret = wss_attr->show(wss, buf);
- return ret;
-}
-/**
- * Sysfs subsystem: forward write calls
- *
- * Sysfs operation for forwarding write call to the store method of the
- * attribute owner
- */
-static
-ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t count)
-{
- struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
- struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
- ssize_t ret = -EIO;
-
- if (wss_attr->store)
- ret = wss_attr->store(wss, buf, count);
- return ret;
-}
-
-static const struct sysfs_ops wss_sysfs_ops = {
- .show = wlp_wss_attr_show,
- .store = wlp_wss_attr_store,
-};
-
-struct kobj_type wss_ktype = {
- .release = wlp_wss_release,
- .sysfs_ops = &wss_sysfs_ops,
-};
-
-
-/**
- * Sysfs files for individual WSS
- */
-
-/**
- * Print static properties of this WSS
- *
- * The name of a WSS may not be null teminated. It's max size is 64 bytes
- * so we copy it to a larger array just to make sure we print sane data.
- */
-static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf)
-{
- int result = 0;
-
- if (mutex_lock_interruptible(&wss->mutex))
- goto out;
- result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
- mutex_unlock(&wss->mutex);
-out:
- return result;
-}
-WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL);
-
-/**
- * Print all connected members of this WSS
- * The EDA cache contains all members of WSS neighborhood.
- */
-static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- return wlp_eda_show(wlp, buf);
-}
-WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL);
-
-static
-const char *__wlp_strstate[] = {
- "none",
- "partially enrolled",
- "enrolled",
- "active",
- "connected",
-};
-
-static const char *wlp_wss_strstate(unsigned state)
-{
- if (state >= ARRAY_SIZE(__wlp_strstate))
- return "unknown state";
- return __wlp_strstate[state];
-}
-
-/*
- * Print current state of this WSS
- */
-static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf)
-{
- int result = 0;
-
- if (mutex_lock_interruptible(&wss->mutex))
- goto out;
- result = scnprintf(buf, PAGE_SIZE, "%s\n",
- wlp_wss_strstate(wss->state));
- mutex_unlock(&wss->mutex);
-out:
- return result;
-}
-WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL);
-
-
-static
-struct attribute *wss_attrs[] = {
- &wss_attr_properties.attr,
- &wss_attr_members.attr,
- &wss_attr_state.attr,
- NULL,
-};
-
-struct attribute_group wss_attr_group = {
- .name = NULL, /* we want them in the same directory */
- .attrs = wss_attrs,
-};
diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c
deleted file mode 100644
index 05dde44b3592..000000000000
--- a/drivers/uwb/wlp/txrx.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * WiMedia Logical Link Control Protocol (WLP)
- * Message exchange infrastructure
- *
- * Copyright (C) 2007 Intel Corporation
- * Reinette Chatre <reinette.chatre@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: Docs
- *
- */
-
-#include <linux/etherdevice.h>
-#include <linux/slab.h>
-#include <linux/wlp.h>
-
-#include "wlp-internal.h"
-
-/*
- * Direct incoming association msg to correct parsing routine
- *
- * We only expect D1, E1, C1, C3 messages as new. All other incoming
- * association messages should form part of an established session that is
- * handled elsewhere.
- * The handling of these messages often require calling sleeping functions
- * - this cannot be done in interrupt context. We use the kernel's
- * workqueue to handle these messages.
- */
-static
-void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
- struct uwb_dev_addr *src)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_frame_assoc *assoc = (void *) skb->data;
- struct wlp_assoc_frame_ctx *frame_ctx;
-
- frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC);
- if (frame_ctx == NULL) {
- dev_err(dev, "WLP: Unable to allocate memory for association "
- "frame handling.\n");
- kfree_skb(skb);
- return;
- }
- frame_ctx->wlp = wlp;
- frame_ctx->skb = skb;
- frame_ctx->src = *src;
- switch (assoc->type) {
- case WLP_ASSOC_D1:
- INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame);
- schedule_work(&frame_ctx->ws);
- break;
- case WLP_ASSOC_E1:
- kfree_skb(skb); /* Temporary until we handle it */
- kfree(frame_ctx); /* Temporary until we handle it */
- break;
- case WLP_ASSOC_C1:
- INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame);
- schedule_work(&frame_ctx->ws);
- break;
- case WLP_ASSOC_C3:
- INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame);
- schedule_work(&frame_ctx->ws);
- break;
- default:
- dev_err(dev, "Received unexpected association frame. "
- "Type = %d \n", assoc->type);
- kfree_skb(skb);
- kfree(frame_ctx);
- break;
- }
-}
-
-/*
- * Process incoming association frame
- *
- * Although it could be possible to deal with some incoming association
- * messages without creating a new session we are keeping things simple. We
- * do not accept new association messages if there is a session in progress
- * and the messages do not belong to that session.
- *
- * If an association message arrives that causes the creation of a session
- * (WLP_ASSOC_E1) while we are in the process of creating a session then we
- * rely on the neighbor mutex to protect the data. That is, the new session
- * will not be started until the previous is completed.
- */
-static
-void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
- struct uwb_dev_addr *src)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_frame_assoc *assoc = (void *) skb->data;
- struct wlp_session *session = wlp->session;
- u8 version;
-
- if (wlp_get_version(wlp, &assoc->version, &version,
- sizeof(assoc->version)) < 0)
- goto error;
- if (version != WLP_VERSION) {
- dev_err(dev, "Unsupported WLP version in association "
- "message.\n");
- goto error;
- }
- if (session != NULL) {
- /* Function that created this session is still holding the
- * &wlp->mutex to protect this session. */
- if (assoc->type == session->exp_message ||
- assoc->type == WLP_ASSOC_F0) {
- if (!memcmp(&session->neighbor_addr, src,
- sizeof(*src))) {
- session->data = skb;
- (session->cb)(wlp);
- } else {
- dev_err(dev, "Received expected message from "
- "unexpected source. Expected message "
- "%d or F0 from %02x:%02x, but received "
- "it from %02x:%02x. Dropping.\n",
- session->exp_message,
- session->neighbor_addr.data[1],
- session->neighbor_addr.data[0],
- src->data[1], src->data[0]);
- goto error;
- }
- } else {
- dev_err(dev, "Association already in progress. "
- "Dropping.\n");
- goto error;
- }
- } else {
- wlp_direct_assoc_frame(wlp, skb, src);
- }
- return;
-error:
- kfree_skb(skb);
-}
-
-/*
- * Verify incoming frame is from connected neighbor, prep to pass to WLP client
- *
- * Verification proceeds according to WLP 0.99 [7.3.1]. The source address
- * is used to determine which neighbor is sending the frame and the WSS tag
- * is used to know to which WSS the frame belongs (we only support one WSS
- * so this test is straight forward).
- * With the WSS found we need to ensure that we are connected before
- * allowing the exchange of data frames.
- */
-static
-int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb,
- struct uwb_dev_addr *src)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = -EINVAL;
- struct wlp_eda_node eda_entry;
- struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data;
-
- /*verify*/
- result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Incoming frame is from unknown "
- "neighbor %02x:%02x.\n", src->data[1],
- src->data[0]);
- goto out;
- }
- if (hdr->tag != eda_entry.tag) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Tag of incoming frame from "
- "%02x:%02x does not match expected tag. "
- "Received 0x%02x, expected 0x%02x. \n",
- src->data[1], src->data[0], hdr->tag,
- eda_entry.tag);
- result = -EINVAL;
- goto out;
- }
- if (eda_entry.state != WLP_WSS_CONNECTED) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Incoming frame from "
- "%02x:%02x does is not from connected WSS.\n",
- src->data[1], src->data[0]);
- result = -EINVAL;
- goto out;
- }
- /*prep*/
- skb_pull(skb, sizeof(*hdr));
-out:
- return result;
-}
-
-/*
- * Receive a WLP frame from device
- *
- * @returns: 1 if calling function should free the skb
- * 0 if it successfully handled skb and freed it
- * 0 if error occured, will free skb in this case
- */
-int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb,
- struct uwb_dev_addr *src)
-{
- unsigned len = skb->len;
- void *ptr = skb->data;
- struct wlp_frame_hdr *hdr;
- int result = 0;
-
- if (len < sizeof(*hdr)) {
- dev_err(dev, "Not enough data to parse WLP header.\n");
- result = -EINVAL;
- goto out;
- }
- hdr = ptr;
- if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) {
- dev_err(dev, "Not a WLP frame type.\n");
- result = -EINVAL;
- goto out;
- }
- switch (hdr->type) {
- case WLP_FRAME_STANDARD:
- if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) {
- dev_err(dev, "Not enough data to parse Standard "
- "WLP header.\n");
- goto out;
- }
- result = wlp_verify_prep_rx_frame(wlp, skb, src);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Verification of frame "
- "from neighbor %02x:%02x failed.\n",
- src->data[1], src->data[0]);
- goto out;
- }
- result = 1;
- break;
- case WLP_FRAME_ABBREVIATED:
- dev_err(dev, "Abbreviated frame received. FIXME?\n");
- kfree_skb(skb);
- break;
- case WLP_FRAME_CONTROL:
- dev_err(dev, "Control frame received. FIXME?\n");
- kfree_skb(skb);
- break;
- case WLP_FRAME_ASSOCIATION:
- if (len < sizeof(struct wlp_frame_assoc)) {
- dev_err(dev, "Not enough data to parse Association "
- "WLP header.\n");
- goto out;
- }
- wlp_receive_assoc_frame(wlp, skb, src);
- break;
- default:
- dev_err(dev, "Invalid frame received.\n");
- result = -EINVAL;
- break;
- }
-out:
- if (result < 0) {
- kfree_skb(skb);
- result = 0;
- }
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_receive_frame);
-
-
-/*
- * Verify frame from network stack, prepare for further transmission
- *
- * @skb: the socket buffer that needs to be prepared for transmission (it
- * is in need of a WLP header). If this is a broadcast frame we take
- * over the entire transmission.
- * If it is a unicast the WSS connection should already be established
- * and transmission will be done by the calling function.
- * @dst: On return this will contain the device address to which the
- * frame is destined.
- * @returns: 0 on success no tx : WLP header successfully applied to skb buffer,
- * calling function can proceed with tx
- * 1 on success with tx : WLP will take over transmission of this
- * frame
- * <0 on error
- *
- * The network stack (WLP client) is attempting to transmit a frame. We can
- * only transmit data if a local WSS is at least active (connection will be
- * done here if this is a broadcast frame and neighbor also has the WSS
- * active).
- *
- * The frame can be either broadcast or unicast. Broadcast in a WSS is
- * supported via multicast, but we don't support multicast yet (until
- * devices start to support MAB IEs). If a broadcast frame needs to be
- * transmitted it is treated as a unicast frame to each neighbor. In this
- * case the WLP takes over transmission of the skb and returns 1
- * to the caller to indicate so. Also, in this case, if a neighbor has the
- * same WSS activated but is not connected then the WSS connection will be
- * done at this time. The neighbor's virtual address will be learned at
- * this time.
- *
- * The destination address in a unicast frame is the virtual address of the
- * neighbor. This address only becomes known when a WSS connection is
- * established. We thus rely on a broadcast frame to trigger the setup of
- * WSS connections to all neighbors before we are able to send unicast
- * frames to them. This seems reasonable as IP would usually use ARP first
- * before any unicast frames are sent.
- *
- * If we are already connected to the neighbor (neighbor's virtual address
- * is known) we just prepare the WLP header and the caller will continue to
- * send the frame.
- *
- * A failure in this function usually indicates something that cannot be
- * fixed automatically. So, if this function fails (@return < 0) the calling
- * function should not retry to send the frame as it will very likely keep
- * failing.
- *
- */
-int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp,
- struct sk_buff *skb, struct uwb_dev_addr *dst)
-{
- int result = -EINVAL;
- struct ethhdr *eth_hdr = (void *) skb->data;
-
- if (is_multicast_ether_addr(eth_hdr->h_dest)) {
- result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "Unable to handle broadcast "
- "frame from WLP client.\n");
- goto out;
- }
- dev_kfree_skb_irq(skb);
- result = 1;
- /* Frame will be transmitted by WLP. */
- } else {
- result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst,
- wlp_wss_prep_hdr, skb);
- if (unlikely(result < 0)) {
- if (printk_ratelimit())
- dev_err(dev, "Unable to prepare "
- "skb for transmission. \n");
- goto out;
- }
- }
-out:
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame);
diff --git a/drivers/uwb/wlp/wlp-internal.h b/drivers/uwb/wlp/wlp-internal.h
deleted file mode 100644
index 3e8d5de7c5b9..000000000000
--- a/drivers/uwb/wlp/wlp-internal.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * WiMedia Logical Link Control Protocol (WLP)
- * Internal API
- *
- * Copyright (C) 2007 Intel Corporation
- * Reinette Chatre <reinette.chatre@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- */
-
-#ifndef __WLP_INTERNAL_H__
-#define __WLP_INTERNAL_H__
-
-/**
- * State of WSS connection
- *
- * A device needs to connect to a neighbor in an activated WSS before data
- * can be transmitted. The spec also distinguishes between a new connection
- * attempt and a connection attempt after previous connection attempts. The
- * state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99
- * [7.2.6]
- */
-enum wlp_wss_connect {
- WLP_WSS_UNCONNECTED = 0,
- WLP_WSS_CONNECTED,
- WLP_WSS_CONNECT_FAILED,
-};
-
-extern struct kobj_type wss_ktype;
-extern struct attribute_group wss_attr_group;
-
-/* This should be changed to a dynamic array where entries are sorted
- * by eth_addr and search is done in a binary form
- *
- * Although thinking twice about it: this technologie's maximum reach
- * is 10 meters...unless you want to pack too much stuff in around
- * your radio controller/WLP device, the list will probably not be
- * too big.
- *
- * In any case, there is probably some data structure in the kernel
- * than we could reused for that already.
- *
- * The below structure is really just good while we support one WSS per
- * host.
- */
-struct wlp_eda_node {
- struct list_head list_node;
- unsigned char eth_addr[ETH_ALEN];
- struct uwb_dev_addr dev_addr;
- struct wlp_wss *wss;
- unsigned char virt_addr[ETH_ALEN];
- u8 tag;
- enum wlp_wss_connect state;
-};
-
-typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *);
-
-extern void wlp_eda_init(struct wlp_eda *);
-extern void wlp_eda_release(struct wlp_eda *);
-extern int wlp_eda_create_node(struct wlp_eda *,
- const unsigned char eth_addr[ETH_ALEN],
- const struct uwb_dev_addr *);
-extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *);
-extern int wlp_eda_update_node(struct wlp_eda *,
- const struct uwb_dev_addr *,
- struct wlp_wss *,
- const unsigned char virt_addr[ETH_ALEN],
- const u8, const enum wlp_wss_connect);
-extern int wlp_eda_update_node_state(struct wlp_eda *,
- const struct uwb_dev_addr *,
- const enum wlp_wss_connect);
-
-extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *,
- struct wlp_eda_node *);
-extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *);
-extern int wlp_eda_for_virtual(struct wlp_eda *,
- const unsigned char eth_addr[ETH_ALEN],
- struct uwb_dev_addr *,
- wlp_eda_for_each_f , void *);
-
-
-extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *);
-
-extern size_t wlp_wss_key_print(char *, size_t, u8 *);
-
-/* Function called when no more references to WSS exists */
-extern void wlp_wss_release(struct kobject *);
-
-extern void wlp_wss_reset(struct wlp_wss *);
-extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *,
- char *, unsigned, unsigned);
-extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *,
- struct uwb_dev_addr *);
-extern ssize_t wlp_discover(struct wlp *);
-
-extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *,
- struct wlp_wss *, struct wlp_uuid *);
-extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *,
- struct uwb_dev_addr *);
-
-struct wlp_assoc_conn_ctx {
- struct work_struct ws;
- struct wlp *wlp;
- struct sk_buff *skb;
- struct wlp_eda_node eda_entry;
-};
-
-
-extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *);
-extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *);
-
-
-/* Message handling */
-struct wlp_assoc_frame_ctx {
- struct work_struct ws;
- struct wlp *wlp;
- struct sk_buff *skb;
- struct uwb_dev_addr src;
-};
-
-extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *);
-extern void wlp_handle_d1_frame(struct work_struct *);
-extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *,
- struct wlp_neighbor_e *);
-extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *,
- struct wlp_neighbor_e *,
- struct wlp_uuid *);
-extern void wlp_handle_c1_frame(struct work_struct *);
-extern void wlp_handle_c3_frame(struct work_struct *);
-extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *,
- struct wlp_uuid *, u8 *,
- struct uwb_mac_addr *);
-extern int wlp_parse_f0(struct wlp *, struct sk_buff *);
-extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *,
- struct uwb_dev_addr *, enum wlp_assoc_type);
-extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *,
- u8 *, ssize_t);
-extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *,
- struct wlp_uuid *, ssize_t);
-extern int __wlp_alloc_device_info(struct wlp *);
-extern int __wlp_setup_device_info(struct wlp *);
-
-extern struct wlp_wss_attribute wss_attribute_properties;
-extern struct wlp_wss_attribute wss_attribute_members;
-extern struct wlp_wss_attribute wss_attribute_state;
-
-static inline
-size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid)
-{
- size_t result;
-
- result = scnprintf(buf, bufsize,
- "%02x:%02x:%02x:%02x:%02x:%02x:"
- "%02x:%02x:%02x:%02x:%02x:%02x:"
- "%02x:%02x:%02x:%02x",
- uuid->data[0], uuid->data[1],
- uuid->data[2], uuid->data[3],
- uuid->data[4], uuid->data[5],
- uuid->data[6], uuid->data[7],
- uuid->data[8], uuid->data[9],
- uuid->data[10], uuid->data[11],
- uuid->data[12], uuid->data[13],
- uuid->data[14], uuid->data[15]);
- return result;
-}
-
-/**
- * FIXME: How should a nonce be displayed?
- */
-static inline
-size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce)
-{
- size_t result;
-
- result = scnprintf(buf, bufsize,
- "%02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x",
- nonce->data[0], nonce->data[1],
- nonce->data[2], nonce->data[3],
- nonce->data[4], nonce->data[5],
- nonce->data[6], nonce->data[7],
- nonce->data[8], nonce->data[9],
- nonce->data[10], nonce->data[11],
- nonce->data[12], nonce->data[13],
- nonce->data[14], nonce->data[15]);
- return result;
-}
-
-
-static inline
-void wlp_session_cb(struct wlp *wlp)
-{
- struct completion *completion = wlp->session->cb_priv;
- complete(completion);
-}
-
-static inline
-int wlp_uuid_is_set(struct wlp_uuid *uuid)
-{
- struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00} };
-
- if (!memcmp(uuid, &zero_uuid, sizeof(*uuid)))
- return 0;
- return 1;
-}
-
-#endif /* __WLP_INTERNAL_H__ */
diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c
deleted file mode 100644
index 7f6a630bf26c..000000000000
--- a/drivers/uwb/wlp/wlp-lc.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
- * WiMedia Logical Link Control Protocol (WLP)
- *
- * Copyright (C) 2005-2006 Intel Corporation
- * Reinette Chatre <reinette.chatre@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * FIXME: docs
- */
-#include <linux/wlp.h>
-#include <linux/slab.h>
-
-#include "wlp-internal.h"
-
-static
-void wlp_neighbor_init(struct wlp_neighbor_e *neighbor)
-{
- INIT_LIST_HEAD(&neighbor->wssid);
-}
-
-/**
- * Create area for device information storage
- *
- * wlp->mutex must be held
- */
-int __wlp_alloc_device_info(struct wlp *wlp)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- BUG_ON(wlp->dev_info != NULL);
- wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
- if (wlp->dev_info == NULL) {
- dev_err(dev, "WLP: Unable to allocate memory for "
- "device information.\n");
- return -ENOMEM;
- }
- return 0;
-}
-
-
-/**
- * Fill in device information using function provided by driver
- *
- * wlp->mutex must be held
- */
-static
-void __wlp_fill_device_info(struct wlp *wlp)
-{
- wlp->fill_device_info(wlp, wlp->dev_info);
-}
-
-/**
- * Setup device information
- *
- * Allocate area for device information and populate it.
- *
- * wlp->mutex must be held
- */
-int __wlp_setup_device_info(struct wlp *wlp)
-{
- int result;
- struct device *dev = &wlp->rc->uwb_dev.dev;
-
- result = __wlp_alloc_device_info(wlp);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to allocate area for "
- "device information.\n");
- return result;
- }
- __wlp_fill_device_info(wlp);
- return 0;
-}
-
-/**
- * Remove information about neighbor stored temporarily
- *
- * Information learned during discovey should only be stored when the
- * device enrolls in the neighbor's WSS. We do need to store this
- * information temporarily in order to present it to the user.
- *
- * We are only interested in keeping neighbor WSS information if that
- * neighbor is accepting enrollment.
- *
- * should be called with wlp->nbmutex held
- */
-void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor)
-{
- struct wlp_wssid_e *wssid_e, *next;
- u8 keep;
- if (!list_empty(&neighbor->wssid)) {
- list_for_each_entry_safe(wssid_e, next, &neighbor->wssid,
- node) {
- if (wssid_e->info != NULL) {
- keep = wssid_e->info->accept_enroll;
- kfree(wssid_e->info);
- wssid_e->info = NULL;
- if (!keep) {
- list_del(&wssid_e->node);
- kfree(wssid_e);
- }
- }
- }
- }
- if (neighbor->info != NULL) {
- kfree(neighbor->info);
- neighbor->info = NULL;
- }
-}
-
-/*
- * Populate WLP neighborhood cache with neighbor information
- *
- * A new neighbor is found. If it is discoverable then we add it to the
- * neighborhood cache.
- *
- */
-static
-int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev)
-{
- int result = 0;
- int discoverable;
- struct wlp_neighbor_e *neighbor;
-
- /*
- * FIXME:
- * Use contents of WLP IE found in beacon cache to determine if
- * neighbor is discoverable.
- * The device does not support WLP IE yet so this still needs to be
- * done. Until then we assume all devices are discoverable.
- */
- discoverable = 1; /* will be changed when FIXME disappears */
- if (discoverable) {
- /* Add neighbor to cache for discovery */
- neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL);
- if (neighbor == NULL) {
- dev_err(&dev->dev, "Unable to create memory for "
- "new neighbor. \n");
- result = -ENOMEM;
- goto error_no_mem;
- }
- wlp_neighbor_init(neighbor);
- uwb_dev_get(dev);
- neighbor->uwb_dev = dev;
- list_add(&neighbor->node, &wlp->neighbors);
- }
-error_no_mem:
- return result;
-}
-
-/**
- * Remove one neighbor from cache
- */
-static
-void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor)
-{
- struct wlp_wssid_e *wssid_e, *next_wssid_e;
-
- list_for_each_entry_safe(wssid_e, next_wssid_e,
- &neighbor->wssid, node) {
- list_del(&wssid_e->node);
- kfree(wssid_e);
- }
- uwb_dev_put(neighbor->uwb_dev);
- list_del(&neighbor->node);
- kfree(neighbor);
-}
-
-/**
- * Clear entire neighborhood cache.
- */
-static
-void __wlp_neighbors_release(struct wlp *wlp)
-{
- struct wlp_neighbor_e *neighbor, *next;
- if (list_empty(&wlp->neighbors))
- return;
- list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
- __wlp_neighbor_release(neighbor);
- }
-}
-
-static
-void wlp_neighbors_release(struct wlp *wlp)
-{
- mutex_lock(&wlp->nbmutex);
- __wlp_neighbors_release(wlp);
- mutex_unlock(&wlp->nbmutex);
-}
-
-
-
-/**
- * Send D1 message to neighbor, receive D2 message
- *
- * @neighbor: neighbor to which D1 message will be sent
- * @wss: if not NULL, it is an enrollment request for this WSS
- * @wssid: if wss not NULL, this is the wssid of the WSS in which we
- * want to enroll
- *
- * A D1/D2 exchange is done for one of two reasons: discovery or
- * enrollment. If done for discovery the D1 message is sent to the neighbor
- * and the contents of the D2 response is stored in a temporary cache.
- * If done for enrollment the @wss and @wssid are provided also. In this
- * case the D1 message is sent to the neighbor, the D2 response is parsed
- * for enrollment of the WSS with wssid.
- *
- * &wss->mutex is held
- */
-static
-int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
- struct wlp_wss *wss, struct wlp_uuid *wssid)
-{
- int result;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- DECLARE_COMPLETION_ONSTACK(completion);
- struct wlp_session session;
- struct sk_buff *skb;
- struct wlp_frame_assoc *resp;
- struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
-
- mutex_lock(&wlp->mutex);
- if (!wlp_uuid_is_set(&wlp->uuid)) {
- dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
- "proceed.\n");
- result = -ENXIO;
- goto out;
- }
- /* Send D1 association frame */
- result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1);
- if (result < 0) {
- dev_err(dev, "Unable to send D1 frame to neighbor "
- "%02x:%02x (%d)\n", dev_addr->data[1],
- dev_addr->data[0], result);
- goto out;
- }
- /* Create session, wait for response */
- session.exp_message = WLP_ASSOC_D2;
- session.cb = wlp_session_cb;
- session.cb_priv = &completion;
- session.neighbor_addr = *dev_addr;
- BUG_ON(wlp->session != NULL);
- wlp->session = &session;
- /* Wait for D2/F0 frame */
- result = wait_for_completion_interruptible_timeout(&completion,
- WLP_PER_MSG_TIMEOUT * HZ);
- if (result == 0) {
- result = -ETIMEDOUT;
- dev_err(dev, "Timeout while sending D1 to neighbor "
- "%02x:%02x.\n", dev_addr->data[1],
- dev_addr->data[0]);
- goto error_session;
- }
- if (result < 0) {
- dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n",
- dev_addr->data[1], dev_addr->data[0]);
- goto error_session;
- }
- /* Parse message in session->data: it will be either D2 or F0 */
- skb = session.data;
- resp = (void *) skb->data;
-
- if (resp->type == WLP_ASSOC_F0) {
- result = wlp_parse_f0(wlp, skb);
- if (result < 0)
- dev_err(dev, "WLP: Unable to parse F0 from neighbor "
- "%02x:%02x.\n", dev_addr->data[1],
- dev_addr->data[0]);
- result = -EINVAL;
- goto error_resp_parse;
- }
- if (wss == NULL) {
- /* Discovery */
- result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to parse D2 message from "
- "neighbor %02x:%02x for discovery.\n",
- dev_addr->data[1], dev_addr->data[0]);
- goto error_resp_parse;
- }
- } else {
- /* Enrollment */
- result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor,
- wssid);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to parse D2 message from "
- "neighbor %02x:%02x for enrollment.\n",
- dev_addr->data[1], dev_addr->data[0]);
- goto error_resp_parse;
- }
- }
-error_resp_parse:
- kfree_skb(skb);
-error_session:
- wlp->session = NULL;
-out:
- mutex_unlock(&wlp->mutex);
- return result;
-}
-
-/**
- * Enroll into WSS of provided WSSID by using neighbor as registrar
- *
- * &wss->mutex is held
- */
-int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
- struct wlp_wss *wss, struct wlp_uuid *wssid)
-{
- int result = 0;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- char buf[WLP_WSS_UUID_STRSIZE];
- struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
-
- wlp_wss_uuid_print(buf, sizeof(buf), wssid);
-
- result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid);
- if (result < 0) {
- dev_err(dev, "WLP: D1/D2 message exchange for enrollment "
- "failed. result = %d \n", result);
- goto out;
- }
- if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
- dev_err(dev, "WLP: Unable to enroll into WSS %s using "
- "neighbor %02x:%02x. \n", buf,
- dev_addr->data[1], dev_addr->data[0]);
- result = -EINVAL;
- goto out;
- }
- if (wss->secure_status == WLP_WSS_SECURE) {
- dev_err(dev, "FIXME: need to complete secure enrollment.\n");
- result = -EINVAL;
- goto error;
- } else {
- wss->state = WLP_WSS_STATE_ENROLLED;
- dev_dbg(dev, "WLP: Success Enrollment into unsecure WSS "
- "%s using neighbor %02x:%02x. \n",
- buf, dev_addr->data[1], dev_addr->data[0]);
- }
-out:
- return result;
-error:
- wlp_wss_reset(wss);
- return result;
-}
-
-/**
- * Discover WSS information of neighbor's active WSS
- */
-static
-int wlp_discover_neighbor(struct wlp *wlp,
- struct wlp_neighbor_e *neighbor)
-{
- return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL);
-}
-
-
-/**
- * Each neighbor in the neighborhood cache is discoverable. Discover it.
- *
- * Discovery is done through sending of D1 association frame and parsing
- * the D2 association frame response. Only wssid from D2 will be included
- * in neighbor cache, rest is just displayed to user and forgotten.
- *
- * The discovery is not done in parallel. This is simple and enables us to
- * maintain only one association context.
- *
- * The discovery of one neighbor does not affect the other, but if the
- * discovery of a neighbor fails it is removed from the neighborhood cache.
- */
-static
-int wlp_discover_all_neighbors(struct wlp *wlp)
-{
- int result = 0;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_neighbor_e *neighbor, *next;
-
- list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
- result = wlp_discover_neighbor(wlp, neighbor);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to discover neighbor "
- "%02x:%02x, removing from neighborhood. \n",
- neighbor->uwb_dev->dev_addr.data[1],
- neighbor->uwb_dev->dev_addr.data[0]);
- __wlp_neighbor_release(neighbor);
- }
- }
- return result;
-}
-
-static int wlp_add_neighbor_helper(struct device *dev, void *priv)
-{
- struct wlp *wlp = priv;
- struct uwb_dev *uwb_dev = to_uwb_dev(dev);
-
- return wlp_add_neighbor(wlp, uwb_dev);
-}
-
-/**
- * Discover WLP neighborhood
- *
- * Will send D1 association frame to all devices in beacon group that have
- * discoverable bit set in WLP IE. D2 frames will be received, information
- * displayed to user in @buf. Partial information (from D2 association
- * frame) will be cached to assist with future association
- * requests.
- *
- * The discovery of the WLP neighborhood is triggered by the user. This
- * should occur infrequently and we thus free current cache and re-allocate
- * memory if needed.
- *
- * If one neighbor fails during initial discovery (determining if it is a
- * neighbor or not), we fail all - note that interaction with neighbor has
- * not occured at this point so if a failure occurs we know something went wrong
- * locally. We thus undo everything.
- */
-ssize_t wlp_discover(struct wlp *wlp)
-{
- int result = 0;
- struct device *dev = &wlp->rc->uwb_dev.dev;
-
- mutex_lock(&wlp->nbmutex);
- /* Clear current neighborhood cache. */
- __wlp_neighbors_release(wlp);
- /* Determine which devices in neighborhood. Repopulate cache. */
- result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp);
- if (result < 0) {
- /* May have partial neighbor information, release all. */
- __wlp_neighbors_release(wlp);
- goto error_dev_for_each;
- }
- /* Discover the properties of devices in neighborhood. */
- result = wlp_discover_all_neighbors(wlp);
- /* In case of failure we still print our partial results. */
- if (result < 0) {
- dev_err(dev, "Unable to fully discover neighborhood. \n");
- result = 0;
- }
-error_dev_for_each:
- mutex_unlock(&wlp->nbmutex);
- return result;
-}
-
-/**
- * Handle events from UWB stack
- *
- * We handle events conservatively. If a neighbor goes off the air we
- * remove it from the neighborhood. If an association process is in
- * progress this function will block waiting for the nbmutex to become
- * free. The association process will thus be allowed to complete before it
- * is removed.
- */
-static
-void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev,
- enum uwb_notifs event)
-{
- struct wlp *wlp = _wlp;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_neighbor_e *neighbor, *next;
- int result;
- switch (event) {
- case UWB_NOTIF_ONAIR:
- result = wlp_eda_create_node(&wlp->eda,
- uwb_dev->mac_addr.data,
- &uwb_dev->dev_addr);
- if (result < 0)
- dev_err(dev, "WLP: Unable to add new neighbor "
- "%02x:%02x to EDA cache.\n",
- uwb_dev->dev_addr.data[1],
- uwb_dev->dev_addr.data[0]);
- break;
- case UWB_NOTIF_OFFAIR:
- wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr);
- mutex_lock(&wlp->nbmutex);
- list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
- if (neighbor->uwb_dev == uwb_dev)
- __wlp_neighbor_release(neighbor);
- }
- mutex_unlock(&wlp->nbmutex);
- break;
- default:
- dev_err(dev, "don't know how to handle event %d from uwb\n",
- event);
- }
-}
-
-static void wlp_channel_changed(struct uwb_pal *pal, int channel)
-{
- struct wlp *wlp = container_of(pal, struct wlp, pal);
-
- if (channel < 0)
- netif_carrier_off(wlp->ndev);
- else
- netif_carrier_on(wlp->ndev);
-}
-
-int wlp_setup(struct wlp *wlp, struct uwb_rc *rc, struct net_device *ndev)
-{
- int result;
-
- BUG_ON(wlp->fill_device_info == NULL);
- BUG_ON(wlp->xmit_frame == NULL);
- BUG_ON(wlp->stop_queue == NULL);
- BUG_ON(wlp->start_queue == NULL);
-
- wlp->rc = rc;
- wlp->ndev = ndev;
- wlp_eda_init(&wlp->eda);/* Set up address cache */
- wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb;
- wlp->uwb_notifs_handler.data = wlp;
- uwb_notifs_register(rc, &wlp->uwb_notifs_handler);
-
- uwb_pal_init(&wlp->pal);
- wlp->pal.rc = rc;
- wlp->pal.channel_changed = wlp_channel_changed;
- result = uwb_pal_register(&wlp->pal);
- if (result < 0)
- uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
-
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_setup);
-
-void wlp_remove(struct wlp *wlp)
-{
- wlp_neighbors_release(wlp);
- uwb_pal_unregister(&wlp->pal);
- uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
- wlp_eda_release(&wlp->eda);
- mutex_lock(&wlp->mutex);
- if (wlp->dev_info != NULL)
- kfree(wlp->dev_info);
- mutex_unlock(&wlp->mutex);
- wlp->rc = NULL;
-}
-EXPORT_SYMBOL_GPL(wlp_remove);
-
-/**
- * wlp_reset_all - reset the WLP hardware
- * @wlp: the WLP device to reset.
- *
- * This schedules a full hardware reset of the WLP device. The radio
- * controller and any other PALs will also be reset.
- */
-void wlp_reset_all(struct wlp *wlp)
-{
- uwb_rc_reset_all(wlp->rc);
-}
-EXPORT_SYMBOL_GPL(wlp_reset_all);
diff --git a/drivers/uwb/wlp/wss-lc.c b/drivers/uwb/wlp/wss-lc.c
deleted file mode 100644
index 67872c83b679..000000000000
--- a/drivers/uwb/wlp/wss-lc.c
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * WiMedia Logical Link Control Protocol (WLP)
- *
- * Copyright (C) 2007 Intel Corporation
- * Reinette Chatre <reinette.chatre@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * Implementation of the WLP association protocol.
- *
- * FIXME: Docs
- *
- * A UWB network interface will configure a WSS through wlp_wss_setup() after
- * the interface has been assigned a MAC address, typically after
- * "ifconfig" has been called. When the interface goes down it should call
- * wlp_wss_remove().
- *
- * When the WSS is ready for use the user interacts via sysfs to create,
- * discover, and activate WSS.
- *
- * wlp_wss_enroll_activate()
- *
- * wlp_wss_create_activate()
- * wlp_wss_set_wssid_hash()
- * wlp_wss_comp_wssid_hash()
- * wlp_wss_sel_bcast_addr()
- * wlp_wss_sysfs_add()
- *
- * Called when no more references to WSS exist:
- * wlp_wss_release()
- * wlp_wss_reset()
- */
-#include <linux/etherdevice.h> /* for is_valid_ether_addr */
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/wlp.h>
-
-#include "wlp-internal.h"
-
-size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key)
-{
- size_t result;
-
- result = scnprintf(buf, bufsize,
- "%02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x",
- key[0], key[1], key[2], key[3],
- key[4], key[5], key[6], key[7],
- key[8], key[9], key[10], key[11],
- key[12], key[13], key[14], key[15]);
- return result;
-}
-
-/**
- * Compute WSSID hash
- * WLP Draft 0.99 [7.2.1]
- *
- * The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR
- * of all octets in the WSSID.
- */
-static
-u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid)
-{
- return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2]
- ^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5]
- ^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8]
- ^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11]
- ^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14]
- ^ wssid->data[15];
-}
-
-/**
- * Select a multicast EUI-48 for the WSS broadcast address.
- * WLP Draft 0.99 [7.2.1]
- *
- * Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP
- * range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive.
- *
- * This address is currently hardcoded.
- * FIXME?
- */
-static
-struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss)
-{
- struct uwb_mac_addr bcast = {
- .data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 }
- };
- return bcast;
-}
-
-/**
- * Clear the contents of the WSS structure - all except kobj, mutex, virtual
- *
- * We do not want to reinitialize - the internal kobj should not change as
- * it still points to the parent received during setup. The mutex should
- * remain also. We thus just reset values individually.
- * The virutal address assigned to WSS will remain the same for the
- * lifetime of the WSS. We only reset the fields that can change during its
- * lifetime.
- */
-void wlp_wss_reset(struct wlp_wss *wss)
-{
- memset(&wss->wssid, 0, sizeof(wss->wssid));
- wss->hash = 0;
- memset(&wss->name[0], 0, sizeof(wss->name));
- memset(&wss->bcast, 0, sizeof(wss->bcast));
- wss->secure_status = WLP_WSS_UNSECURE;
- memset(&wss->master_key[0], 0, sizeof(wss->master_key));
- wss->tag = 0;
- wss->state = WLP_WSS_STATE_NONE;
-}
-
-/**
- * Create sysfs infrastructure for WSS
- *
- * The WSS is configured to have the interface as parent (see wlp_wss_setup())
- * a new sysfs directory that includes wssid as its name is created in the
- * interface's sysfs directory. The group of files interacting with WSS are
- * created also.
- */
-static
-int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result;
-
- result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str);
- if (result < 0)
- return result;
- wss->kobj.ktype = &wss_ktype;
- result = kobject_init_and_add(&wss->kobj,
- &wss_ktype, wss->kobj.parent, "wlp");
- if (result < 0) {
- dev_err(dev, "WLP: Cannot register WSS kobject.\n");
- goto error_kobject_register;
- }
- result = sysfs_create_group(&wss->kobj, &wss_attr_group);
- if (result < 0) {
- dev_err(dev, "WLP: Cannot register WSS attributes: %d\n",
- result);
- goto error_sysfs_create_group;
- }
- return 0;
-error_sysfs_create_group:
-
- kobject_put(&wss->kobj); /* will free name if needed */
- return result;
-error_kobject_register:
- kfree(wss->kobj.name);
- wss->kobj.name = NULL;
- wss->kobj.ktype = NULL;
- return result;
-}
-
-
-/**
- * Release WSS
- *
- * No more references exist to this WSS. We should undo everything that was
- * done in wlp_wss_create_activate() except removing the group. The group
- * is not removed because an object can be unregistered before the group is
- * created. We also undo any additional operations on the WSS after this
- * (addition of members).
- *
- * If memory was allocated for the kobject's name then it will
- * be freed by the kobject system during this time.
- *
- * The EDA cache is removed and reinitialized when the WSS is removed. We
- * thus loose knowledge of members of this WSS at that time and need not do
- * it here.
- */
-void wlp_wss_release(struct kobject *kobj)
-{
- struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj);
-
- wlp_wss_reset(wss);
-}
-
-/**
- * Enroll into a WSS using provided neighbor as registrar
- *
- * First search the neighborhood information to learn which neighbor is
- * referred to, next proceed with enrollment.
- *
- * &wss->mutex is held
- */
-static
-int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid,
- struct uwb_dev_addr *dest)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_neighbor_e *neighbor;
- int result = -ENXIO;
- struct uwb_dev_addr *dev_addr;
-
- mutex_lock(&wlp->nbmutex);
- list_for_each_entry(neighbor, &wlp->neighbors, node) {
- dev_addr = &neighbor->uwb_dev->dev_addr;
- if (!memcmp(dest, dev_addr, sizeof(*dest))) {
- result = wlp_enroll_neighbor(wlp, neighbor, wss, wssid);
- break;
- }
- }
- if (result == -ENXIO)
- dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n",
- dest->data[1], dest->data[0]);
- mutex_unlock(&wlp->nbmutex);
- return result;
-}
-
-/**
- * Enroll into a WSS previously discovered
- *
- * User provides WSSID of WSS, search for neighbor that has this WSS
- * activated and attempt to enroll.
- *
- * &wss->mutex is held
- */
-static
-int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_neighbor_e *neighbor;
- struct wlp_wssid_e *wssid_e;
- char buf[WLP_WSS_UUID_STRSIZE];
- int result = -ENXIO;
-
-
- mutex_lock(&wlp->nbmutex);
- list_for_each_entry(neighbor, &wlp->neighbors, node) {
- list_for_each_entry(wssid_e, &neighbor->wssid, node) {
- if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) {
- result = wlp_enroll_neighbor(wlp, neighbor,
- wss, wssid);
- if (result == 0) /* enrollment success */
- goto out;
- break;
- }
- }
- }
-out:
- if (result == -ENXIO) {
- wlp_wss_uuid_print(buf, sizeof(buf), wssid);
- dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf);
- }
- mutex_unlock(&wlp->nbmutex);
- return result;
-}
-
-/**
- * Enroll into WSS with provided WSSID, registrar may be provided
- *
- * @wss: out WSS that will be enrolled
- * @wssid: wssid of neighboring WSS that we want to enroll in
- * @devaddr: registrar can be specified, will be broadcast (ff:ff) if any
- * neighbor can be used as registrar.
- *
- * &wss->mutex is held
- */
-static
-int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid,
- struct uwb_dev_addr *devaddr)
-{
- int result;
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- char buf[WLP_WSS_UUID_STRSIZE];
- struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
-
- wlp_wss_uuid_print(buf, sizeof(buf), wssid);
-
- if (wss->state != WLP_WSS_STATE_NONE) {
- dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf);
- result = -EEXIST;
- goto error;
- }
- if (!memcmp(&bcast, devaddr, sizeof(bcast)))
- result = wlp_wss_enroll_discovered(wss, wssid);
- else
- result = wlp_wss_enroll_target(wss, wssid, devaddr);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n",
- buf, result);
- goto error;
- }
- dev_dbg(dev, "Successfully enrolled into WSS %s \n", buf);
- result = wlp_wss_sysfs_add(wss, buf);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n");
- wlp_wss_reset(wss);
- }
-error:
- return result;
-
-}
-
-/**
- * Activate given WSS
- *
- * Prior to activation a WSS must be enrolled. To activate a WSS a device
- * includes the WSS hash in the WLP IE in its beacon in each superframe.
- * WLP 0.99 [7.2.5].
- *
- * The WSS tag is also computed at this time. We only support one activated
- * WSS so we can use the hash as a tag - there will never be a conflict.
- *
- * We currently only support one activated WSS so only one WSS hash is
- * included in the WLP IE.
- */
-static
-int wlp_wss_activate(struct wlp_wss *wss)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct uwb_rc *uwb_rc = wlp->rc;
- int result;
- struct {
- struct wlp_ie wlp_ie;
- u8 hash; /* only include one hash */
- } ie_data;
-
- BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED);
- wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid);
- wss->tag = wss->hash;
- memset(&ie_data, 0, sizeof(ie_data));
- ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP;
- ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr);
- wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash));
- ie_data.hash = wss->hash;
- result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr,
- sizeof(ie_data));
- if (result < 0) {
- dev_err(dev, "WLP: Unable to add WLP IE to beacon. "
- "result = %d.\n", result);
- goto error_wlp_ie;
- }
- wss->state = WLP_WSS_STATE_ACTIVE;
- result = 0;
-error_wlp_ie:
- return result;
-}
-
-/**
- * Enroll in and activate WSS identified by provided WSSID
- *
- * The neighborhood cache should contain a list of all neighbors and the
- * WSS they have activated. Based on that cache we search which neighbor we
- * can perform the association process with. The user also has option to
- * specify which neighbor it prefers as registrar.
- * Successful enrollment is followed by activation.
- * Successful activation will create the sysfs directory containing
- * specific information regarding this WSS.
- */
-int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
- struct uwb_dev_addr *devaddr)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = 0;
- char buf[WLP_WSS_UUID_STRSIZE];
-
- mutex_lock(&wss->mutex);
- result = wlp_wss_enroll(wss, wssid, devaddr);
- if (result < 0) {
- wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
- dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf);
- goto error_enroll;
- }
- result = wlp_wss_activate(wss);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment "
- "result = %d \n", result);
- /* Undo enrollment */
- wlp_wss_reset(wss);
- goto error_activate;
- }
-error_activate:
-error_enroll:
- mutex_unlock(&wss->mutex);
- return result;
-}
-
-/**
- * Create, enroll, and activate a new WSS
- *
- * @wssid: new wssid provided by user
- * @name: WSS name requested by used.
- * @sec_status: security status requested by user
- *
- * A user requested the creation of a new WSS. All operations are done
- * locally. The new WSS will be stored locally, the hash will be included
- * in the WLP IE, and the sysfs infrastructure for this WSS will be
- * created.
- */
-int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
- char *name, unsigned sec_status, unsigned accept)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = 0;
- char buf[WLP_WSS_UUID_STRSIZE];
-
- result = wlp_wss_uuid_print(buf, sizeof(buf), wssid);
-
- if (!mutex_trylock(&wss->mutex)) {
- dev_err(dev, "WLP: WLP association session in progress.\n");
- return -EBUSY;
- }
- if (wss->state != WLP_WSS_STATE_NONE) {
- dev_err(dev, "WLP: WSS already exists. Not creating new.\n");
- result = -EEXIST;
- goto out;
- }
- if (wss->kobj.parent == NULL) {
- dev_err(dev, "WLP: WSS parent not ready. Is network interface "
- "up?\n");
- result = -ENXIO;
- goto out;
- }
- if (sec_status == WLP_WSS_SECURE) {
- dev_err(dev, "WLP: FIXME Creation of secure WSS not "
- "supported yet.\n");
- result = -EINVAL;
- goto out;
- }
- wss->wssid = *wssid;
- memcpy(wss->name, name, sizeof(wss->name));
- wss->bcast = wlp_wss_sel_bcast_addr(wss);
- wss->secure_status = sec_status;
- wss->accept_enroll = accept;
- /*wss->virtual_addr is initialized in call to wlp_wss_setup*/
- /* sysfs infrastructure */
- result = wlp_wss_sysfs_add(wss, buf);
- if (result < 0) {
- dev_err(dev, "Cannot set up sysfs for WSS kobject.\n");
- wlp_wss_reset(wss);
- goto out;
- } else
- result = 0;
- wss->state = WLP_WSS_STATE_ENROLLED;
- result = wlp_wss_activate(wss);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to activate WSS. Undoing "
- "enrollment\n");
- wlp_wss_reset(wss);
- goto out;
- }
- result = 0;
-out:
- mutex_unlock(&wss->mutex);
- return result;
-}
-
-/**
- * Determine if neighbor has WSS activated
- *
- * @returns: 1 if neighbor has WSS activated, zero otherwise
- *
- * This can be done in two ways:
- * - send a C1 frame, parse C2/F0 response
- * - examine the WLP IE sent by the neighbor
- *
- * The WLP IE is not fully supported in hardware so we use the C1/C2 frame
- * exchange to determine if a WSS is activated. Using the WLP IE should be
- * faster and should be used when it becomes possible.
- */
-int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss,
- struct uwb_dev_addr *dev_addr)
-{
- int result = 0;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- DECLARE_COMPLETION_ONSTACK(completion);
- struct wlp_session session;
- struct sk_buff *skb;
- struct wlp_frame_assoc *resp;
- struct wlp_uuid wssid;
-
- mutex_lock(&wlp->mutex);
- /* Send C1 association frame */
- result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1);
- if (result < 0) {
- dev_err(dev, "Unable to send C1 frame to neighbor "
- "%02x:%02x (%d)\n", dev_addr->data[1],
- dev_addr->data[0], result);
- result = 0;
- goto out;
- }
- /* Create session, wait for response */
- session.exp_message = WLP_ASSOC_C2;
- session.cb = wlp_session_cb;
- session.cb_priv = &completion;
- session.neighbor_addr = *dev_addr;
- BUG_ON(wlp->session != NULL);
- wlp->session = &session;
- /* Wait for C2/F0 frame */
- result = wait_for_completion_interruptible_timeout(&completion,
- WLP_PER_MSG_TIMEOUT * HZ);
- if (result == 0) {
- dev_err(dev, "Timeout while sending C1 to neighbor "
- "%02x:%02x.\n", dev_addr->data[1],
- dev_addr->data[0]);
- goto out;
- }
- if (result < 0) {
- dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n",
- dev_addr->data[1], dev_addr->data[0]);
- result = 0;
- goto out;
- }
- /* Parse message in session->data: it will be either C2 or F0 */
- skb = session.data;
- resp = (void *) skb->data;
- if (resp->type == WLP_ASSOC_F0) {
- result = wlp_parse_f0(wlp, skb);
- if (result < 0)
- dev_err(dev, "WLP: unable to parse incoming F0 "
- "frame from neighbor %02x:%02x.\n",
- dev_addr->data[1], dev_addr->data[0]);
- result = 0;
- goto error_resp_parse;
- }
- /* WLP version and message type fields have already been parsed */
- result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid,
- skb->len - sizeof(*resp));
- if (result < 0) {
- dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n");
- result = 0;
- goto error_resp_parse;
- }
- if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)))
- result = 1;
- else {
- dev_err(dev, "WLP: Received a C2 frame without matching "
- "WSSID.\n");
- result = 0;
- }
-error_resp_parse:
- kfree_skb(skb);
-out:
- wlp->session = NULL;
- mutex_unlock(&wlp->mutex);
- return result;
-}
-
-/**
- * Activate connection with neighbor by updating EDA cache
- *
- * @wss: local WSS to which neighbor wants to connect
- * @dev_addr: neighbor's address
- * @wssid: neighbor's WSSID - must be same as our WSS's WSSID
- * @tag: neighbor's WSS tag used to identify frames transmitted by it
- * @virt_addr: neighbor's virtual EUI-48
- */
-static
-int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss,
- struct uwb_dev_addr *dev_addr,
- struct wlp_uuid *wssid, u8 *tag,
- struct uwb_mac_addr *virt_addr)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = 0;
-
- if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) {
- /* Update EDA cache */
- result = wlp_eda_update_node(&wlp->eda, dev_addr, wss,
- (void *) virt_addr->data, *tag,
- WLP_WSS_CONNECTED);
- if (result < 0)
- dev_err(dev, "WLP: Unable to update EDA cache "
- "with new connected neighbor information.\n");
- } else {
- dev_err(dev, "WLP: Neighbor does not have matching WSSID.\n");
- result = -EINVAL;
- }
- return result;
-}
-
-/**
- * Connect to WSS neighbor
- *
- * Use C3/C4 exchange to determine if neighbor has WSS activated and
- * retrieve the WSS tag and virtual EUI-48 of the neighbor.
- */
-static
-int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss,
- struct uwb_dev_addr *dev_addr)
-{
- int result;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct wlp_uuid wssid;
- u8 tag;
- struct uwb_mac_addr virt_addr;
- DECLARE_COMPLETION_ONSTACK(completion);
- struct wlp_session session;
- struct wlp_frame_assoc *resp;
- struct sk_buff *skb;
-
- mutex_lock(&wlp->mutex);
- /* Send C3 association frame */
- result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3);
- if (result < 0) {
- dev_err(dev, "Unable to send C3 frame to neighbor "
- "%02x:%02x (%d)\n", dev_addr->data[1],
- dev_addr->data[0], result);
- goto out;
- }
- /* Create session, wait for response */
- session.exp_message = WLP_ASSOC_C4;
- session.cb = wlp_session_cb;
- session.cb_priv = &completion;
- session.neighbor_addr = *dev_addr;
- BUG_ON(wlp->session != NULL);
- wlp->session = &session;
- /* Wait for C4/F0 frame */
- result = wait_for_completion_interruptible_timeout(&completion,
- WLP_PER_MSG_TIMEOUT * HZ);
- if (result == 0) {
- dev_err(dev, "Timeout while sending C3 to neighbor "
- "%02x:%02x.\n", dev_addr->data[1],
- dev_addr->data[0]);
- result = -ETIMEDOUT;
- goto out;
- }
- if (result < 0) {
- dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n",
- dev_addr->data[1], dev_addr->data[0]);
- goto out;
- }
- /* Parse message in session->data: it will be either C4 or F0 */
- skb = session.data;
- resp = (void *) skb->data;
- if (resp->type == WLP_ASSOC_F0) {
- result = wlp_parse_f0(wlp, skb);
- if (result < 0)
- dev_err(dev, "WLP: unable to parse incoming F0 "
- "frame from neighbor %02x:%02x.\n",
- dev_addr->data[1], dev_addr->data[0]);
- result = -EINVAL;
- goto error_resp_parse;
- }
- result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n");
- goto error_resp_parse;
- }
- result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag,
- &virt_addr);
- if (result < 0) {
- dev_err(dev, "WLP: Unable to activate connection to "
- "neighbor %02x:%02x.\n", dev_addr->data[1],
- dev_addr->data[0]);
- goto error_resp_parse;
- }
-error_resp_parse:
- kfree_skb(skb);
-out:
- /* Record that we unsuccessfully tried to connect to this neighbor */
- if (result < 0)
- wlp_eda_update_node_state(&wlp->eda, dev_addr,
- WLP_WSS_CONNECT_FAILED);
- wlp->session = NULL;
- mutex_unlock(&wlp->mutex);
- return result;
-}
-
-/**
- * Connect to neighbor with common WSS, send pending frame
- *
- * This function is scheduled when a frame is destined to a neighbor with
- * which we do not have a connection. A copy of the EDA cache entry is
- * provided - not the actual cache entry (because it is protected by a
- * spinlock).
- *
- * First determine if neighbor has the same WSS activated, connect if it
- * does. The C3/C4 exchange is dual purpose to determine if neighbor has
- * WSS activated and proceed with the connection.
- *
- * The frame that triggered the connection setup is sent after connection
- * setup.
- *
- * network queue is stopped - we need to restart when done
- *
- */
-static
-void wlp_wss_connect_send(struct work_struct *ws)
-{
- struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws,
- struct wlp_assoc_conn_ctx,
- ws);
- struct wlp *wlp = conn_ctx->wlp;
- struct sk_buff *skb = conn_ctx->skb;
- struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry;
- struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
- struct wlp_wss *wss = &wlp->wss;
- int result;
- struct device *dev = &wlp->rc->uwb_dev.dev;
-
- mutex_lock(&wss->mutex);
- if (wss->state < WLP_WSS_STATE_ACTIVE) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Attempting to connect with "
- "WSS that is not active or connected.\n");
- dev_kfree_skb(skb);
- goto out;
- }
- /* Establish connection - send C3 rcv C4 */
- result = wlp_wss_connect_neighbor(wlp, wss, dev_addr);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Unable to establish connection "
- "with neighbor %02x:%02x.\n",
- dev_addr->data[1], dev_addr->data[0]);
- dev_kfree_skb(skb);
- goto out;
- }
- /* EDA entry changed, update the local copy being used */
- result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Cannot find EDA entry for "
- "neighbor %02x:%02x \n",
- dev_addr->data[1], dev_addr->data[0]);
- }
- result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Unable to prepare frame header for "
- "transmission (neighbor %02x:%02x). \n",
- dev_addr->data[1], dev_addr->data[0]);
- dev_kfree_skb(skb);
- goto out;
- }
- BUG_ON(wlp->xmit_frame == NULL);
- result = wlp->xmit_frame(wlp, skb, dev_addr);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Unable to transmit frame: %d\n",
- result);
- if (result == -ENXIO)
- dev_err(dev, "WLP: Is network interface up? \n");
- /* We could try again ... */
- dev_kfree_skb(skb);/*we need to free if tx fails */
- }
-out:
- kfree(conn_ctx);
- BUG_ON(wlp->start_queue == NULL);
- wlp->start_queue(wlp);
- mutex_unlock(&wss->mutex);
-}
-
-/**
- * Add WLP header to outgoing skb
- *
- * @eda_entry: pointer to neighbor's entry in the EDA cache
- * @_skb: skb containing data destined to the neighbor
- */
-int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry,
- void *_skb)
-{
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = 0;
- unsigned char *eth_addr = eda_entry->eth_addr;
- struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
- struct sk_buff *skb = _skb;
- struct wlp_frame_std_abbrv_hdr *std_hdr;
-
- if (eda_entry->state == WLP_WSS_CONNECTED) {
- /* Add WLP header */
- BUG_ON(skb_headroom(skb) < sizeof(*std_hdr));
- std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr));
- std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
- std_hdr->hdr.type = WLP_FRAME_STANDARD;
- std_hdr->tag = eda_entry->wss->tag;
- } else {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Destination neighbor (Ethernet: "
- "%pM, Dev: %02x:%02x) is not connected.\n",
- eth_addr, dev_addr->data[1], dev_addr->data[0]);
- result = -EINVAL;
- }
- return result;
-}
-
-
-/**
- * Prepare skb for neighbor: connect if not already and prep WLP header
- *
- * This function is called in interrupt context, but it needs to sleep. We
- * temporarily stop the net queue to establish the WLP connection.
- * Setup of the WLP connection and restart of queue is scheduled
- * on the default work queue.
- *
- * run with eda->lock held (spinlock)
- */
-int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry,
- void *_skb)
-{
- int result = 0;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct sk_buff *skb = _skb;
- struct wlp_assoc_conn_ctx *conn_ctx;
-
- if (eda_entry->state == WLP_WSS_UNCONNECTED) {
- /* We don't want any more packets while we set up connection */
- BUG_ON(wlp->stop_queue == NULL);
- wlp->stop_queue(wlp);
- conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC);
- if (conn_ctx == NULL) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Unable to allocate memory "
- "for connection handling.\n");
- result = -ENOMEM;
- goto out;
- }
- conn_ctx->wlp = wlp;
- conn_ctx->skb = skb;
- conn_ctx->eda_entry = *eda_entry;
- INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send);
- schedule_work(&conn_ctx->ws);
- result = 1;
- } else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) {
- /* Previous connection attempts failed, don't retry - see
- * conditions for connection in WLP 0.99 [7.6.2] */
- if (printk_ratelimit())
- dev_err(dev, "Could not connect to neighbor "
- "previously. Not retrying. \n");
- result = -ENONET;
- goto out;
- } else /* eda_entry->state == WLP_WSS_CONNECTED */
- result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
-out:
- return result;
-}
-
-/**
- * Emulate broadcast: copy skb, send copy to neighbor (connect if not already)
- *
- * We need to copy skbs in the case where we emulate broadcast through
- * unicast. We copy instead of clone because we are modifying the data of
- * the frame after copying ... clones share data so we cannot emulate
- * broadcast using clones.
- *
- * run with eda->lock held (spinlock)
- */
-int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry,
- void *_skb)
-{
- int result = -ENOMEM;
- struct device *dev = &wlp->rc->uwb_dev.dev;
- struct sk_buff *skb = _skb;
- struct sk_buff *copy;
- struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
-
- copy = skb_copy(skb, GFP_ATOMIC);
- if (copy == NULL) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Unable to copy skb for "
- "transmission.\n");
- goto out;
- }
- result = wlp_wss_connect_prep(wlp, eda_entry, copy);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Unable to connect/send skb "
- "to neighbor.\n");
- dev_kfree_skb_irq(copy);
- goto out;
- } else if (result == 1)
- /* Frame will be transmitted separately */
- goto out;
- BUG_ON(wlp->xmit_frame == NULL);
- result = wlp->xmit_frame(wlp, copy, dev_addr);
- if (result < 0) {
- if (printk_ratelimit())
- dev_err(dev, "WLP: Unable to transmit frame: %d\n",
- result);
- if ((result == -ENXIO) && printk_ratelimit())
- dev_err(dev, "WLP: Is network interface up? \n");
- /* We could try again ... */
- dev_kfree_skb_irq(copy);/*we need to free if tx fails */
- }
-out:
- return result;
-}
-
-
-/**
- * Setup WSS
- *
- * Should be called by network driver after the interface has been given a
- * MAC address.
- */
-int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
- struct device *dev = &wlp->rc->uwb_dev.dev;
- int result = 0;
-
- mutex_lock(&wss->mutex);
- wss->kobj.parent = &net_dev->dev.kobj;
- if (!is_valid_ether_addr(net_dev->dev_addr)) {
- dev_err(dev, "WLP: Invalid MAC address. Cannot use for"
- "virtual.\n");
- result = -EINVAL;
- goto out;
- }
- memcpy(wss->virtual_addr.data, net_dev->dev_addr,
- sizeof(wss->virtual_addr.data));
-out:
- mutex_unlock(&wss->mutex);
- return result;
-}
-EXPORT_SYMBOL_GPL(wlp_wss_setup);
-
-/**
- * Remove WSS
- *
- * Called by client that configured WSS through wlp_wss_setup(). This
- * function is called when client no longer needs WSS, eg. client shuts
- * down.
- *
- * We remove the WLP IE from the beacon before initiating local cleanup.
- */
-void wlp_wss_remove(struct wlp_wss *wss)
-{
- struct wlp *wlp = container_of(wss, struct wlp, wss);
-
- mutex_lock(&wss->mutex);
- if (wss->state == WLP_WSS_STATE_ACTIVE)
- uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP);
- if (wss->state != WLP_WSS_STATE_NONE) {
- sysfs_remove_group(&wss->kobj, &wss_attr_group);
- kobject_put(&wss->kobj);
- }
- wss->kobj.parent = NULL;
- memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr));
- /* Cleanup EDA cache */
- wlp_eda_release(&wlp->eda);
- wlp_eda_init(&wlp->eda);
- mutex_unlock(&wss->mutex);
-}
-EXPORT_SYMBOL_GPL(wlp_wss_remove);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index dc06ff134559..27c1fb4b1e0d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -17,6 +17,8 @@ source "drivers/gpu/vga/Kconfig"
source "drivers/gpu/drm/Kconfig"
+source "drivers/gpu/stub/Kconfig"
+
config VGASTATE
tristate
default n
@@ -1919,6 +1921,9 @@ config FB_SH_MOBILE_HDMI
tristate "SuperH Mobile HDMI controller support"
depends on FB_SH_MOBILE_LCDC
select FB_MODE_HELPERS
+ select SOUND
+ select SND
+ select SND_SOC
---help---
Driver for the on-chip SH-Mobile HDMI controller.
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index f8d69ad36830..5bf91236c701 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -2970,7 +2970,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev,
struct atyfb_par *par = info->par;
struct device_node *dp;
char prop[128];
- int node, len, i, j, ret;
+ phandle node;
+ int len, i, j, ret;
u32 mem, chip_id;
/*
diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c
index e77e8e4280fb..4ea187d93768 100644
--- a/drivers/video/au1200fb.c
+++ b/drivers/video/au1200fb.c
@@ -1079,7 +1079,7 @@ static int au1200fb_fb_check_var(struct fb_var_screeninfo *var,
* clock can only be obtain by dividing this value by an even integer.
* Fallback to a slower pixel clock if necessary. */
pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin);
- pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2));
+ pixclock = min3(pixclock, fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2);
if (AU1200_LCD_MAX_CLK % pixclock) {
int diff = AU1200_LCD_MAX_CLK % pixclock;
diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c
index ca75cc2a87a5..d7c6c3e0afc6 100644
--- a/drivers/video/omap/lcd_omap3beagle.c
+++ b/drivers/video/omap/lcd_omap3beagle.c
@@ -25,8 +25,6 @@
#include <linux/gpio.h>
#include <linux/i2c/twl.h>
-#include <plat/mux.h>
-#include <plat/mux.h>
#include <asm/mach-types.h>
#include "omapfb.h"
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index 881c9f77c75a..12327bbfdbbb 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -40,7 +40,7 @@ config PANEL_TPO_TD043MTEA1
config PANEL_ACX565AKM
tristate "ACX565AKM Panel"
- depends on OMAP2_DSS_SDI
+ depends on OMAP2_DSS_SDI && SPI
select BACKLIGHT_CLASS_DEVICE
help
This is the LCD panel used on Nokia N900
diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c
index 07fbb8a733bb..e77310653207 100644
--- a/drivers/video/omap2/displays/panel-acx565akm.c
+++ b/drivers/video/omap2/displays/panel-acx565akm.c
@@ -587,6 +587,9 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev)
dev_dbg(&dssdev->dev, "%s\n", __func__);
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
mutex_lock(&md->mutex);
r = omapdss_sdi_display_enable(dssdev);
@@ -644,6 +647,9 @@ static void acx_panel_power_off(struct omap_dss_device *dssdev)
dev_dbg(&dssdev->dev, "%s\n", __func__);
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
mutex_lock(&md->mutex);
if (!md->enabled) {
diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c
index 300eff5de1b4..395a68de3990 100644
--- a/drivers/video/omap2/displays/panel-generic.c
+++ b/drivers/video/omap2/displays/panel-generic.c
@@ -39,6 +39,9 @@ static int generic_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
@@ -58,6 +61,9 @@ err0:
static void generic_panel_power_off(struct omap_dss_device *dssdev)
{
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
diff --git a/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c b/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c
index 10267461991c..0c6896cea2d0 100644
--- a/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c
+++ b/drivers/video/omap2/displays/panel-sharp-lq043t1dg01.c
@@ -43,6 +43,9 @@ static int sharp_lq_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
@@ -65,6 +68,9 @@ err0:
static void sharp_lq_panel_power_off(struct omap_dss_device *dssdev)
{
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
index 7d9eb2b1f5af..9a138f650e05 100644
--- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
+++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
@@ -135,6 +135,9 @@ static int sharp_ls_power_on(struct omap_dss_device *dssdev)
{
int r = 0;
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
@@ -157,6 +160,9 @@ err0:
static void sharp_ls_power_off(struct omap_dss_device *dssdev)
{
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
diff --git a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c b/drivers/video/omap2/displays/panel-toppoly-tdo35s.c
index e320e67d06f3..526e906c8a6c 100644
--- a/drivers/video/omap2/displays/panel-toppoly-tdo35s.c
+++ b/drivers/video/omap2/displays/panel-toppoly-tdo35s.c
@@ -46,6 +46,9 @@ static int toppoly_tdo_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
@@ -65,6 +68,9 @@ err0:
static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev)
{
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
index e866e76b13d0..dbe9d43b4850 100644
--- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
@@ -269,6 +269,9 @@ static int tpo_td043_power_on(struct omap_dss_device *dssdev)
int nreset_gpio = dssdev->reset_gpio;
int r;
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
@@ -308,6 +311,9 @@ static void tpo_td043_power_off(struct omap_dss_device *dssdev)
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int nreset_gpio = dssdev->reset_gpio;
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
tpo_td043_write(tpo_td043->spi, 3,
TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index d71b5d9d71b1..7db17b5e570c 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
-omapdss-y := core.o dss.o dispc.o display.o manager.o overlay.o
+omapdss-y := core.o dss.o dss_features.o dispc.o display.o manager.o overlay.o
omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o
omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index b3a498f22d36..8e89f6049280 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -37,6 +37,7 @@
#include <plat/clock.h>
#include "dss.h"
+#include "dss_features.h"
static struct {
struct platform_device *pdev;
@@ -502,6 +503,8 @@ static int omap_dss_probe(struct platform_device *pdev)
core.pdev = pdev;
+ dss_features_init();
+
dss_init_overlay_managers(pdev);
dss_init_overlays(pdev);
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 5ecdc0004094..fa40fa59a9ac 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -39,6 +39,7 @@
#include <plat/display.h>
#include "dss.h"
+#include "dss_features.h"
/* DISPC */
#define DISPC_BASE 0x48050400
@@ -139,6 +140,22 @@ struct omap_dispc_isr_data {
u32 mask;
};
+struct dispc_h_coef {
+ s8 hc4;
+ s8 hc3;
+ u8 hc2;
+ s8 hc1;
+ s8 hc0;
+};
+
+struct dispc_v_coef {
+ s8 vc22;
+ s8 vc2;
+ u8 vc1;
+ s8 vc0;
+ s8 vc00;
+};
+
#define REG_GET(idx, start, end) \
FLD_GET(dispc_read_reg(idx), start, end)
@@ -564,106 +581,77 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
int vscaleup, int five_taps)
{
/* Coefficients for horizontal up-sampling */
- static const u32 coef_hup[8] = {
- 0x00800000,
- 0x0D7CF800,
- 0x1E70F5FF,
- 0x335FF5FE,
- 0xF74949F7,
- 0xF55F33FB,
- 0xF5701EFE,
- 0xF87C0DFF,
+ static const struct dispc_h_coef coef_hup[8] = {
+ { 0, 0, 128, 0, 0 },
+ { -1, 13, 124, -8, 0 },
+ { -2, 30, 112, -11, -1 },
+ { -5, 51, 95, -11, -2 },
+ { 0, -9, 73, 73, -9 },
+ { -2, -11, 95, 51, -5 },
+ { -1, -11, 112, 30, -2 },
+ { 0, -8, 124, 13, -1 },
};
- /* Coefficients for horizontal down-sampling */
- static const u32 coef_hdown[8] = {
- 0x24382400,
- 0x28371FFE,
- 0x2C361BFB,
- 0x303516F9,
- 0x11343311,
- 0x1635300C,
- 0x1B362C08,
- 0x1F372804,
+ /* Coefficients for vertical up-sampling */
+ static const struct dispc_v_coef coef_vup_3tap[8] = {
+ { 0, 0, 128, 0, 0 },
+ { 0, 3, 123, 2, 0 },
+ { 0, 12, 111, 5, 0 },
+ { 0, 32, 89, 7, 0 },
+ { 0, 0, 64, 64, 0 },
+ { 0, 7, 89, 32, 0 },
+ { 0, 5, 111, 12, 0 },
+ { 0, 2, 123, 3, 0 },
};
- /* Coefficients for horizontal and vertical up-sampling */
- static const u32 coef_hvup[2][8] = {
- {
- 0x00800000,
- 0x037B02FF,
- 0x0C6F05FE,
- 0x205907FB,
- 0x00404000,
- 0x075920FE,
- 0x056F0CFF,
- 0x027B0300,
- },
- {
- 0x00800000,
- 0x0D7CF8FF,
- 0x1E70F5FE,
- 0x335FF5FB,
- 0xF7404000,
- 0xF55F33FE,
- 0xF5701EFF,
- 0xF87C0D00,
- },
+ static const struct dispc_v_coef coef_vup_5tap[8] = {
+ { 0, 0, 128, 0, 0 },
+ { -1, 13, 124, -8, 0 },
+ { -2, 30, 112, -11, -1 },
+ { -5, 51, 95, -11, -2 },
+ { 0, -9, 73, 73, -9 },
+ { -2, -11, 95, 51, -5 },
+ { -1, -11, 112, 30, -2 },
+ { 0, -8, 124, 13, -1 },
};
- /* Coefficients for horizontal and vertical down-sampling */
- static const u32 coef_hvdown[2][8] = {
- {
- 0x24382400,
- 0x28391F04,
- 0x2D381B08,
- 0x3237170C,
- 0x123737F7,
- 0x173732F9,
- 0x1B382DFB,
- 0x1F3928FE,
- },
- {
- 0x24382400,
- 0x28371F04,
- 0x2C361B08,
- 0x3035160C,
- 0x113433F7,
- 0x163530F9,
- 0x1B362CFB,
- 0x1F3728FE,
- },
+ /* Coefficients for horizontal down-sampling */
+ static const struct dispc_h_coef coef_hdown[8] = {
+ { 0, 36, 56, 36, 0 },
+ { 4, 40, 55, 31, -2 },
+ { 8, 44, 54, 27, -5 },
+ { 12, 48, 53, 22, -7 },
+ { -9, 17, 52, 51, 17 },
+ { -7, 22, 53, 48, 12 },
+ { -5, 27, 54, 44, 8 },
+ { -2, 31, 55, 40, 4 },
};
- /* Coefficients for vertical up-sampling */
- static const u32 coef_vup[8] = {
- 0x00000000,
- 0x0000FF00,
- 0x0000FEFF,
- 0x0000FBFE,
- 0x000000F7,
- 0x0000FEFB,
- 0x0000FFFE,
- 0x000000FF,
+ /* Coefficients for vertical down-sampling */
+ static const struct dispc_v_coef coef_vdown_3tap[8] = {
+ { 0, 36, 56, 36, 0 },
+ { 0, 40, 57, 31, 0 },
+ { 0, 45, 56, 27, 0 },
+ { 0, 50, 55, 23, 0 },
+ { 0, 18, 55, 55, 0 },
+ { 0, 23, 55, 50, 0 },
+ { 0, 27, 56, 45, 0 },
+ { 0, 31, 57, 40, 0 },
};
-
- /* Coefficients for vertical down-sampling */
- static const u32 coef_vdown[8] = {
- 0x00000000,
- 0x000004FE,
- 0x000008FB,
- 0x00000CF9,
- 0x0000F711,
- 0x0000F90C,
- 0x0000FB08,
- 0x0000FE04,
+ static const struct dispc_v_coef coef_vdown_5tap[8] = {
+ { 0, 36, 56, 36, 0 },
+ { 4, 40, 55, 31, -2 },
+ { 8, 44, 54, 27, -5 },
+ { 12, 48, 53, 22, -7 },
+ { -9, 17, 52, 51, 17 },
+ { -7, 22, 53, 48, 12 },
+ { -5, 27, 54, 44, 8 },
+ { -2, 31, 55, 40, 4 },
};
- const u32 *h_coef;
- const u32 *hv_coef;
- const u32 *hv_coef_mod;
- const u32 *v_coef;
+ const struct dispc_h_coef *h_coef;
+ const struct dispc_v_coef *v_coef;
int i;
if (hscaleup)
@@ -671,47 +659,34 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
else
h_coef = coef_hdown;
- if (vscaleup) {
- hv_coef = coef_hvup[five_taps];
- v_coef = coef_vup;
-
- if (hscaleup)
- hv_coef_mod = NULL;
- else
- hv_coef_mod = coef_hvdown[five_taps];
- } else {
- hv_coef = coef_hvdown[five_taps];
- v_coef = coef_vdown;
-
- if (hscaleup)
- hv_coef_mod = coef_hvup[five_taps];
- else
- hv_coef_mod = NULL;
- }
+ if (vscaleup)
+ v_coef = five_taps ? coef_vup_5tap : coef_vup_3tap;
+ else
+ v_coef = five_taps ? coef_vdown_5tap : coef_vdown_3tap;
for (i = 0; i < 8; i++) {
u32 h, hv;
- h = h_coef[i];
-
- hv = hv_coef[i];
-
- if (hv_coef_mod) {
- hv &= 0xffffff00;
- hv |= (hv_coef_mod[i] & 0xff);
- }
+ h = FLD_VAL(h_coef[i].hc0, 7, 0)
+ | FLD_VAL(h_coef[i].hc1, 15, 8)
+ | FLD_VAL(h_coef[i].hc2, 23, 16)
+ | FLD_VAL(h_coef[i].hc3, 31, 24);
+ hv = FLD_VAL(h_coef[i].hc4, 7, 0)
+ | FLD_VAL(v_coef[i].vc0, 15, 8)
+ | FLD_VAL(v_coef[i].vc1, 23, 16)
+ | FLD_VAL(v_coef[i].vc2, 31, 24);
_dispc_write_firh_reg(plane, i, h);
_dispc_write_firhv_reg(plane, i, hv);
}
- if (!five_taps)
- return;
-
- for (i = 0; i < 8; i++) {
- u32 v;
- v = v_coef[i];
- _dispc_write_firv_reg(plane, i, v);
+ if (five_taps) {
+ for (i = 0; i < 8; i++) {
+ u32 v;
+ v = FLD_VAL(v_coef[i].vc00, 7, 0)
+ | FLD_VAL(v_coef[i].vc22, 15, 8);
+ _dispc_write_firv_reg(plane, i, v);
+ }
}
}
@@ -800,12 +775,12 @@ static void _dispc_set_vid_size(enum omap_plane plane, int width, int height)
static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha)
{
-
- BUG_ON(plane == OMAP_DSS_VIDEO1);
-
- if (cpu_is_omap24xx())
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return;
+ BUG_ON(!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
+ plane == OMAP_DSS_VIDEO1);
+
if (plane == OMAP_DSS_GFX)
REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0);
else if (plane == OMAP_DSS_VIDEO2)
@@ -975,17 +950,14 @@ static void dispc_read_plane_fifo_sizes(void)
DISPC_VID_FIFO_SIZE_STATUS(1) };
u32 size;
int plane;
+ u8 start, end;
enable_clocks(1);
- for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
- if (cpu_is_omap24xx())
- size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0);
- else if (cpu_is_omap34xx())
- size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0);
- else
- BUG();
+ dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
+ for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
+ size = FLD_GET(dispc_read_reg(fsz_reg[plane]), start, end);
dispc.fifo_size[plane] = size;
}
@@ -1002,6 +974,8 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD,
DISPC_VID_FIFO_THRESHOLD(0),
DISPC_VID_FIFO_THRESHOLD(1) };
+ u8 hi_start, hi_end, lo_start, lo_end;
+
enable_clocks(1);
DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",
@@ -1010,12 +984,12 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
REG_GET(ftrs_reg[plane], 27, 16),
low, high);
- if (cpu_is_omap24xx())
- dispc_write_reg(ftrs_reg[plane],
- FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0));
- else
- dispc_write_reg(ftrs_reg[plane],
- FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0));
+ dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);
+ dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end);
+
+ dispc_write_reg(ftrs_reg[plane],
+ FLD_VAL(high, hi_start, hi_end) |
+ FLD_VAL(low, lo_start, lo_end));
enable_clocks(0);
}
@@ -1035,13 +1009,16 @@ static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc)
u32 val;
const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0),
DISPC_VID_FIR(1) };
+ u8 hinc_start, hinc_end, vinc_start, vinc_end;
BUG_ON(plane == OMAP_DSS_GFX);
- if (cpu_is_omap24xx())
- val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0);
- else
- val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0);
+ dss_feat_get_reg_field(FEAT_REG_FIRHINC, &hinc_start, &hinc_end);
+ dss_feat_get_reg_field(FEAT_REG_FIRVINC, &vinc_start, &vinc_end);
+
+ val = FLD_VAL(vinc, vinc_start, vinc_end) |
+ FLD_VAL(hinc, hinc_start, hinc_end);
+
dispc_write_reg(fir_reg[plane-1], val);
}
@@ -1567,6 +1544,8 @@ static int _dispc_setup_plane(enum omap_plane plane,
case OMAP_DSS_COLOR_ARGB16:
case OMAP_DSS_COLOR_ARGB32:
case OMAP_DSS_COLOR_RGBA32:
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
+ return -EINVAL;
case OMAP_DSS_COLOR_RGBX32:
if (cpu_is_omap24xx())
return -EINVAL;
@@ -1607,9 +1586,10 @@ static int _dispc_setup_plane(enum omap_plane plane,
case OMAP_DSS_COLOR_ARGB16:
case OMAP_DSS_COLOR_ARGB32:
case OMAP_DSS_COLOR_RGBA32:
- if (cpu_is_omap24xx())
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return -EINVAL;
- if (plane == OMAP_DSS_VIDEO1)
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
+ plane == OMAP_DSS_VIDEO1)
return -EINVAL;
break;
@@ -2002,7 +1982,7 @@ void dispc_enable_trans_key(enum omap_channel ch, bool enable)
}
void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)
{
- if (cpu_is_omap24xx())
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return;
enable_clocks(1);
@@ -2016,7 +1996,7 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch)
{
bool enabled;
- if (cpu_is_omap24xx())
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return false;
enable_clocks(1);
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index b3fa3a7db911..aa4f7a5fae29 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -3274,7 +3274,6 @@ int dsi_init(struct platform_device *pdev)
dsi.vdds_dsi_reg = dss_get_vdds_dsi();
if (IS_ERR(dsi.vdds_dsi_reg)) {
- iounmap(dsi.base);
DSSERR("can't get VDDS_DSI regulator\n");
r = PTR_ERR(dsi.vdds_dsi_reg);
goto err2;
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
new file mode 100644
index 000000000000..867f68de125f
--- /dev/null
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -0,0 +1,191 @@
+/*
+ * linux/drivers/video/omap2/dss/dss_features.c
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Archit Taneja <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss_features.h"
+
+/* Defines a generic omap register field */
+struct dss_reg_field {
+ enum dss_feat_reg_field id;
+ u8 start, end;
+};
+
+struct omap_dss_features {
+ const struct dss_reg_field *reg_fields;
+ const int num_reg_fields;
+
+ const u32 has_feature;
+
+ const int num_mgrs;
+ const int num_ovls;
+ const enum omap_display_type *supported_displays;
+ const enum omap_color_mode *supported_color_modes;
+};
+
+/* This struct is assigned to one of the below during initialization */
+static struct omap_dss_features *omap_current_dss_features;
+
+static const struct dss_reg_field omap2_dss_reg_fields[] = {
+ { FEAT_REG_FIRHINC, 11, 0 },
+ { FEAT_REG_FIRVINC, 27, 16 },
+ { FEAT_REG_FIFOLOWTHRESHOLD, 8, 0 },
+ { FEAT_REG_FIFOHIGHTHRESHOLD, 24, 16 },
+ { FEAT_REG_FIFOSIZE, 8, 0 },
+};
+
+static const struct dss_reg_field omap3_dss_reg_fields[] = {
+ { FEAT_REG_FIRHINC, 12, 0 },
+ { FEAT_REG_FIRVINC, 28, 16 },
+ { FEAT_REG_FIFOLOWTHRESHOLD, 11, 0 },
+ { FEAT_REG_FIFOHIGHTHRESHOLD, 27, 16 },
+ { FEAT_REG_FIFOSIZE, 10, 0 },
+};
+
+static const enum omap_display_type omap2_dss_supported_displays[] = {
+ /* OMAP_DSS_CHANNEL_LCD */
+ OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
+ OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI,
+
+ /* OMAP_DSS_CHANNEL_DIGIT */
+ OMAP_DISPLAY_TYPE_VENC,
+};
+
+static const enum omap_display_type omap3_dss_supported_displays[] = {
+ /* OMAP_DSS_CHANNEL_LCD */
+ OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
+ OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI,
+
+ /* OMAP_DSS_CHANNEL_DIGIT */
+ OMAP_DISPLAY_TYPE_VENC,
+};
+
+static const enum omap_color_mode omap2_dss_supported_color_modes[] = {
+ /* OMAP_DSS_GFX */
+ OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
+ OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
+ OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
+ OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P,
+
+ /* OMAP_DSS_VIDEO1 */
+ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+ OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
+ OMAP_DSS_COLOR_UYVY,
+
+ /* OMAP_DSS_VIDEO2 */
+ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+ OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
+ OMAP_DSS_COLOR_UYVY,
+};
+
+static const enum omap_color_mode omap3_dss_supported_color_modes[] = {
+ /* OMAP_DSS_GFX */
+ OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
+ OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
+ OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
+ 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_VIDEO1 */
+ OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P |
+ OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
+ OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY,
+
+ /* OMAP_DSS_VIDEO2 */
+ OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
+ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+ OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
+ OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 |
+ OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
+};
+
+/* OMAP2 DSS Features */
+static struct omap_dss_features omap2_dss_features = {
+ .reg_fields = omap2_dss_reg_fields,
+ .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields),
+
+ .num_mgrs = 2,
+ .num_ovls = 3,
+ .supported_displays = omap2_dss_supported_displays,
+ .supported_color_modes = omap2_dss_supported_color_modes,
+};
+
+/* OMAP3 DSS Features */
+static struct omap_dss_features omap3_dss_features = {
+ .reg_fields = omap3_dss_reg_fields,
+ .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
+
+ .has_feature = FEAT_GLOBAL_ALPHA,
+
+ .num_mgrs = 2,
+ .num_ovls = 3,
+ .supported_displays = omap3_dss_supported_displays,
+ .supported_color_modes = omap3_dss_supported_color_modes,
+};
+
+/* Functions returning values related to a DSS feature */
+int dss_feat_get_num_mgrs(void)
+{
+ return omap_current_dss_features->num_mgrs;
+}
+
+int dss_feat_get_num_ovls(void)
+{
+ return omap_current_dss_features->num_ovls;
+}
+
+enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel)
+{
+ return omap_current_dss_features->supported_displays[channel];
+}
+
+enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane)
+{
+ return omap_current_dss_features->supported_color_modes[plane];
+}
+
+/* DSS has_feature check */
+bool dss_has_feature(enum dss_feat_id id)
+{
+ return omap_current_dss_features->has_feature & id;
+}
+
+void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end)
+{
+ if (id >= omap_current_dss_features->num_reg_fields)
+ BUG();
+
+ *start = omap_current_dss_features->reg_fields[id].start;
+ *end = omap_current_dss_features->reg_fields[id].end;
+}
+
+void dss_features_init(void)
+{
+ if (cpu_is_omap24xx())
+ omap_current_dss_features = &omap2_dss_features;
+ else
+ omap_current_dss_features = &omap3_dss_features;
+}
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
new file mode 100644
index 000000000000..cb231eaa9b31
--- /dev/null
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -0,0 +1,50 @@
+/*
+ * linux/drivers/video/omap2/dss/dss_features.h
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Archit Taneja <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OMAP2_DSS_FEATURES_H
+#define __OMAP2_DSS_FEATURES_H
+
+#define MAX_DSS_MANAGERS 2
+#define MAX_DSS_OVERLAYS 3
+
+/* DSS has feature id */
+enum dss_feat_id {
+ FEAT_GLOBAL_ALPHA = 1 << 0,
+ FEAT_GLOBAL_ALPHA_VID1 = 1 << 1,
+};
+
+/* DSS register field id */
+enum dss_feat_reg_field {
+ FEAT_REG_FIRHINC,
+ FEAT_REG_FIRVINC,
+ FEAT_REG_FIFOHIGHTHRESHOLD,
+ FEAT_REG_FIFOLOWTHRESHOLD,
+ FEAT_REG_FIFOSIZE,
+};
+
+/* DSS Feature Functions */
+int dss_feat_get_num_mgrs(void);
+int dss_feat_get_num_ovls(void);
+enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel);
+enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane);
+
+bool dss_has_feature(enum dss_feat_id id);
+void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
+void dss_features_init(void);
+#endif
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index 6a649ab5539e..545e9b9a4d92 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -33,6 +33,7 @@
#include <plat/cpu.h>
#include "dss.h"
+#include "dss_features.h"
static int num_managers;
static struct list_head manager_list;
@@ -448,8 +449,8 @@ struct manager_cache_data {
static struct {
spinlock_t lock;
- struct overlay_cache_data overlay_cache[3];
- struct manager_cache_data manager_cache[2];
+ struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS];
+ struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
bool irq_enabled;
} dss_cache;
@@ -882,12 +883,12 @@ static int configure_dispc(void)
{
struct overlay_cache_data *oc;
struct manager_cache_data *mc;
- const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
- const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+ const int num_ovls = dss_feat_get_num_ovls();
+ const int num_mgrs = dss_feat_get_num_mgrs();
int i;
int r;
- bool mgr_busy[2];
- bool mgr_go[2];
+ bool mgr_busy[MAX_DSS_MANAGERS];
+ bool mgr_go[MAX_DSS_MANAGERS];
bool busy;
r = 0;
@@ -989,7 +990,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,
{
struct overlay_cache_data *oc;
struct manager_cache_data *mc;
- const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+ const int num_ovls = dss_feat_get_num_ovls();
struct omap_overlay_manager *mgr;
int i;
u16 x, y, w, h;
@@ -1121,8 +1122,8 @@ void dss_start_update(struct omap_dss_device *dssdev)
{
struct manager_cache_data *mc;
struct overlay_cache_data *oc;
- const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
- const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+ const int num_ovls = dss_feat_get_num_ovls();
+ const int num_mgrs = dss_feat_get_num_mgrs();
struct omap_overlay_manager *mgr;
int i;
@@ -1151,10 +1152,10 @@ static void dss_apply_irq_handler(void *data, u32 mask)
{
struct manager_cache_data *mc;
struct overlay_cache_data *oc;
- const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
- const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+ const int num_ovls = dss_feat_get_num_ovls();
+ const int num_mgrs = dss_feat_get_num_mgrs();
int i, r;
- bool mgr_busy[2];
+ bool mgr_busy[MAX_DSS_MANAGERS];
mgr_busy[0] = dispc_go_busy(0);
mgr_busy[1] = dispc_go_busy(1);
@@ -1461,7 +1462,7 @@ int dss_init_overlay_managers(struct platform_device *pdev)
num_managers = 0;
- for (i = 0; i < 2; ++i) {
+ for (i = 0; i < dss_feat_get_num_mgrs(); ++i) {
struct omap_overlay_manager *mgr;
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
@@ -1471,14 +1472,10 @@ int dss_init_overlay_managers(struct platform_device *pdev)
case 0:
mgr->name = "lcd";
mgr->id = OMAP_DSS_CHANNEL_LCD;
- mgr->supported_displays =
- OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
- OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI;
break;
case 1:
mgr->name = "tv";
mgr->id = OMAP_DSS_CHANNEL_DIGIT;
- mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC;
break;
}
@@ -1494,6 +1491,8 @@ int dss_init_overlay_managers(struct platform_device *pdev)
mgr->disable = &dss_mgr_disable;
mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC;
+ mgr->supported_displays =
+ dss_feat_get_supported_displays(mgr->id);
dss_overlay_setup_dispc_manager(mgr);
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index 244dca81a399..75642c22cac7 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -35,6 +35,7 @@
#include <plat/cpu.h>
#include "dss.h"
+#include "dss_features.h"
static int num_overlays;
static struct list_head overlay_list;
@@ -237,7 +238,8 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
/* Video1 plane does not support global alpha
* to always make it 255 completely opaque
*/
- if (ovl->id == OMAP_DSS_VIDEO1)
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
+ ovl->id == OMAP_DSS_VIDEO1)
info.global_alpha = 255;
else
info.global_alpha = simple_strtoul(buf, NULL, 10);
@@ -510,11 +512,11 @@ static void omap_dss_add_overlay(struct omap_overlay *overlay)
list_add_tail(&overlay->list, &overlay_list);
}
-static struct omap_overlay *dispc_overlays[3];
+static struct omap_overlay *dispc_overlays[MAX_DSS_OVERLAYS];
void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
{
- mgr->num_overlays = 3;
+ mgr->num_overlays = dss_feat_get_num_ovls();
mgr->overlays = dispc_overlays;
}
@@ -535,7 +537,7 @@ void dss_init_overlays(struct platform_device *pdev)
num_overlays = 0;
- for (i = 0; i < 3; ++i) {
+ for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
struct omap_overlay *ovl;
ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
@@ -545,18 +547,12 @@ void dss_init_overlays(struct platform_device *pdev)
case 0:
ovl->name = "gfx";
ovl->id = OMAP_DSS_GFX;
- ovl->supported_modes = cpu_is_omap34xx() ?
- OMAP_DSS_COLOR_GFX_OMAP3 :
- OMAP_DSS_COLOR_GFX_OMAP2;
ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
break;
case 1:
ovl->name = "vid1";
ovl->id = OMAP_DSS_VIDEO1;
- ovl->supported_modes = cpu_is_omap34xx() ?
- OMAP_DSS_COLOR_VID1_OMAP3 :
- OMAP_DSS_COLOR_VID_OMAP2;
ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
@@ -564,9 +560,6 @@ void dss_init_overlays(struct platform_device *pdev)
case 2:
ovl->name = "vid2";
ovl->id = OMAP_DSS_VIDEO2;
- ovl->supported_modes = cpu_is_omap34xx() ?
- OMAP_DSS_COLOR_VID2_OMAP3 :
- OMAP_DSS_COLOR_VID_OMAP2;
ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
@@ -579,6 +572,9 @@ void dss_init_overlays(struct platform_device *pdev)
ovl->get_overlay_info = &dss_ovl_get_overlay_info;
ovl->wait_for_go = &dss_ovl_wait_for_go;
+ ovl->supported_modes =
+ dss_feat_get_supported_color_modes(ovl->id);
+
omap_dss_add_overlay(ovl);
r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
@@ -651,7 +647,7 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
}
if (mgr) {
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < dss_feat_get_num_ovls(); i++) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(i);
if (!ovl->manager || force) {
diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig
index 43496d6c377f..65149b22cf37 100644
--- a/drivers/video/omap2/omapfb/Kconfig
+++ b/drivers/video/omap2/omapfb/Kconfig
@@ -3,7 +3,7 @@ menuconfig FB_OMAP2
depends on FB && OMAP2_DSS
select OMAP2_VRAM
- select OMAP2_VRFB
+ select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 04034d410d6d..6a704f176c22 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -714,10 +714,10 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
var->pixclock = timings.pixel_clock != 0 ?
KHZ2PICOS(timings.pixel_clock) :
0;
- var->left_margin = timings.hfp;
- var->right_margin = timings.hbp;
- var->upper_margin = timings.vfp;
- var->lower_margin = timings.vbp;
+ var->left_margin = timings.hbp;
+ var->right_margin = timings.hfp;
+ var->upper_margin = timings.vbp;
+ var->lower_margin = timings.vfp;
var->hsync_len = timings.hsw;
var->vsync_len = timings.vsw;
} else {
@@ -2059,10 +2059,10 @@ static int omapfb_mode_to_timings(const char *mode_str,
if (r != 0) {
timings->pixel_clock = PICOS2KHZ(var.pixclock);
- timings->hfp = var.left_margin;
- timings->hbp = var.right_margin;
- timings->vfp = var.upper_margin;
- timings->vbp = var.lower_margin;
+ timings->hbp = var.left_margin;
+ timings->hfp = var.right_margin;
+ timings->vbp = var.upper_margin;
+ timings->vfp = var.lower_margin;
timings->hsw = var.hsync_len;
timings->vsw = var.vsync_len;
timings->x_res = var.xres;
@@ -2198,6 +2198,16 @@ static int omapfb_probe(struct platform_device *pdev)
goto err0;
}
+ /* TODO : Replace cpu check with omap_has_vrfb once HAS_FEATURE
+ * available for OMAP2 and OMAP3
+ */
+ if (def_vrfb && !cpu_is_omap24xx() && !cpu_is_omap34xx()) {
+ def_vrfb = 0;
+ dev_warn(&pdev->dev, "VRFB is not supported on this hardware, "
+ "ignoring the module parameter vrfb=y\n");
+ }
+
+
mutex_init(&fbdev->mtx);
fbdev->dev = &pdev->dev;
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
index 2fde08cc66bf..ef989d94511c 100644
--- a/drivers/video/sh_mobile_hdmi.c
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -22,6 +22,8 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/workqueue.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
#include <video/sh_mobile_hdmi.h>
#include <video/sh_mobile_lcdc.h>
@@ -222,6 +224,58 @@ static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg)
return ioread8(hdmi->base + reg);
}
+/*
+ * HDMI sound
+ */
+static unsigned int sh_hdmi_snd_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct sh_hdmi *hdmi = snd_soc_codec_get_drvdata(codec);
+
+ return hdmi_read(hdmi, reg);
+}
+
+static int sh_hdmi_snd_write(struct snd_soc_codec *codec,
+ unsigned int reg,
+ unsigned int value)
+{
+ struct sh_hdmi *hdmi = snd_soc_codec_get_drvdata(codec);
+
+ hdmi_write(hdmi, value, reg);
+ return 0;
+}
+
+static struct snd_soc_dai_driver sh_hdmi_dai = {
+ .name = "sh_mobile_hdmi-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+
+static int sh_hdmi_snd_probe(struct snd_soc_codec *codec)
+{
+ dev_info(codec->dev, "SH Mobile HDMI Audio Codec");
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = {
+ .probe = sh_hdmi_snd_probe,
+ .read = sh_hdmi_snd_read,
+ .write = sh_hdmi_snd_write,
+};
+
+/*
+ * HDMI video
+ */
+
/* External video parameter settings */
static void hdmi_external_video_param(struct sh_hdmi *hdmi)
{
@@ -318,6 +372,9 @@ static void sh_hdmi_video_config(struct sh_hdmi *hdmi)
*/
static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
{
+ u8 data;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
/*
* [7:4] L/R data swap control
* [3:0] appropriate N[19:16]
@@ -335,7 +392,23 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
* [6:5] set required down sampling rate if required
* [4:3] set required audio source
*/
- hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1);
+ switch (pdata->flags & HDMI_SND_SRC_MASK) {
+ default:
+ /* fall through */
+ case HDMI_SND_SRC_I2S:
+ data = 0x0 << 3;
+ break;
+ case HDMI_SND_SRC_SPDIF:
+ data = 0x1 << 3;
+ break;
+ case HDMI_SND_SRC_DSD:
+ data = 0x2 << 3;
+ break;
+ case HDMI_SND_SRC_HBR:
+ data = 0x3 << 3;
+ break;
+ }
+ hdmi_write(hdmi, data, HDMI_AUDIO_SETTING_1);
/* [3:0] set sending channel number for channel status */
hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2);
@@ -891,6 +964,11 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ ret = snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_sh_hdmi, &sh_hdmi_dai, 1);
+ if (ret < 0)
+ goto esndreg;
+
hdmi->dev = &pdev->dev;
hdmi->hdmi_clk = clk_get(&pdev->dev, "ick");
@@ -976,6 +1054,8 @@ eclkenable:
erate:
clk_put(hdmi->hdmi_clk);
egetclk:
+ snd_soc_unregister_codec(&pdev->dev);
+esndreg:
kfree(hdmi);
return ret;
@@ -988,6 +1068,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev)
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
+ snd_soc_unregister_codec(&pdev->dev);
+
pdata->lcd_chan->board_cfg.display_on = NULL;
pdata->lcd_chan->board_cfg.display_off = NULL;
pdata->lcd_chan->board_cfg.board_data = NULL;
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index d72075a9f01c..7a1419279c8f 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -1243,8 +1243,10 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
if (priv->ch[i].sglist)
vfree(priv->ch[i].sglist);
- dma_free_coherent(&pdev->dev, info->fix.smem_len,
- info->screen_base, priv->ch[i].dma_handle);
+ 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);
}
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index 0c9ce88e95e8..68bd23476c64 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -32,10 +32,14 @@
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
+#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/xilinxfb.h>
#include <linux/slab.h>
+
+#ifdef CONFIG_PPC_DCR
#include <asm/dcr.h>
+#endif
#define DRIVER_NAME "xilinxfb"
@@ -123,10 +127,10 @@ struct xilinxfb_drvdata {
registers */
void __iomem *regs; /* virt. address of the control
registers */
-
+#ifdef CONFIG_PPC_DCR
dcr_host_t dcr_host;
unsigned int dcr_len;
-
+#endif
void *fb_virt; /* virt. address of the frame buffer */
dma_addr_t fb_phys; /* phys. address of the frame buffer */
int fb_alloced; /* Flag, was the fb memory alloced? */
@@ -152,9 +156,10 @@ static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset,
{
if (drvdata->flags & PLB_ACCESS_FLAG)
out_be32(drvdata->regs + (offset << 2), val);
+#ifdef CONFIG_PPC_DCR
else
dcr_write(drvdata->dcr_host, offset, val);
-
+#endif
}
static int
@@ -383,8 +388,11 @@ static int xilinxfb_release(struct device *dev)
if (drvdata->flags & PLB_ACCESS_FLAG) {
iounmap(drvdata->regs);
release_mem_region(drvdata->regs_phys, 8);
- } else
+ }
+#ifdef CONFIG_PPC_DCR
+ else
dcr_unmap(drvdata->dcr_host, drvdata->dcr_len);
+#endif
kfree(drvdata);
dev_set_drvdata(dev, NULL);
@@ -404,7 +412,7 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match)
u32 tft_access;
struct xilinxfb_platform_data pdata;
struct resource res;
- int size, rc, start;
+ int size, rc;
struct xilinxfb_drvdata *drvdata;
/* Copy with the default pdata (not a ptr reference!) */
@@ -437,7 +445,10 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match)
dev_err(&op->dev, "invalid address\n");
goto err;
}
- } else {
+ }
+#ifdef CONFIG_PPC_DCR
+ else {
+ int start;
res.start = 0;
start = dcr_resource_start(op->dev.of_node, 0);
drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0);
@@ -447,6 +458,7 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match)
goto err;
}
}
+#endif
prop = of_get_property(op->dev.of_node, "phys-size", &size);
if ((prop) && (size >= sizeof(u32)*2)) {
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 81e3d6100894..3dd4971160ef 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -38,11 +38,11 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
-#include <linux/clk.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
#include <mach/hardware.h>
#include <plat/prcm.h>
@@ -61,8 +61,6 @@ struct omap_wdt_dev {
void __iomem *base; /* physical */
struct device *dev;
int omap_wdt_users;
- struct clk *ick;
- struct clk *fck;
struct resource *mem;
struct miscdevice omap_wdt_miscdev;
};
@@ -146,8 +144,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file)
if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
return -EBUSY;
- clk_enable(wdev->ick); /* Enable the interface clock */
- clk_enable(wdev->fck); /* Enable the functional clock */
+ pm_runtime_get_sync(wdev->dev);
/* initialize prescaler */
while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
@@ -177,8 +174,7 @@ static int omap_wdt_release(struct inode *inode, struct file *file)
omap_wdt_disable(wdev);
- clk_disable(wdev->ick);
- clk_disable(wdev->fck);
+ pm_runtime_put_sync(wdev->dev);
#else
printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
#endif
@@ -293,19 +289,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev)
wdev->omap_wdt_users = 0;
wdev->mem = mem;
-
- wdev->ick = clk_get(&pdev->dev, "ick");
- if (IS_ERR(wdev->ick)) {
- ret = PTR_ERR(wdev->ick);
- wdev->ick = NULL;
- goto err_clk;
- }
- wdev->fck = clk_get(&pdev->dev, "fck");
- if (IS_ERR(wdev->fck)) {
- ret = PTR_ERR(wdev->fck);
- wdev->fck = NULL;
- goto err_clk;
- }
+ wdev->dev = &pdev->dev;
wdev->base = ioremap(res->start, resource_size(res));
if (!wdev->base) {
@@ -315,8 +299,8 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wdev);
- clk_enable(wdev->ick);
- clk_enable(wdev->fck);
+ pm_runtime_enable(wdev->dev);
+ pm_runtime_get_sync(wdev->dev);
omap_wdt_disable(wdev);
omap_wdt_adjust_timeout(timer_margin);
@@ -334,11 +318,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev)
__raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
timer_margin);
- /* autogate OCP interface clock */
- __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG);
-
- clk_disable(wdev->ick);
- clk_disable(wdev->fck);
+ pm_runtime_put_sync(wdev->dev);
omap_wdt_dev = pdev;
@@ -350,12 +330,6 @@ err_misc:
err_ioremap:
wdev->base = NULL;
-
-err_clk:
- if (wdev->ick)
- clk_put(wdev->ick);
- if (wdev->fck)
- clk_put(wdev->fck);
kfree(wdev);
err_kzalloc:
@@ -387,8 +361,6 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev)
release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
- clk_put(wdev->ick);
- clk_put(wdev->fck);
iounmap(wdev->base);
kfree(wdev);
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 7d24b0d94ed4..347f17edad77 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -261,7 +261,7 @@ static void init_evtchn_cpu_bindings(void)
}
#endif
- memset(cpu_evtchn_mask(0), ~0, sizeof(cpu_evtchn_mask(0)));
+ memset(cpu_evtchn_mask(0), ~0, sizeof(struct cpu_evtchn_s));
}
static inline void clear_evtchn(int port)
@@ -377,7 +377,7 @@ int bind_evtchn_to_irq(unsigned int evtchn)
irq = find_unbound_irq();
set_irq_chip_and_handler_name(irq, &xen_dynamic_chip,
- handle_edge_irq, "event");
+ handle_fasteoi_irq, "event");
evtchn_to_irq[evtchn] = irq;
irq_info[irq] = mk_evtchn_info(evtchn);
@@ -435,6 +435,11 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
irq = per_cpu(virq_to_irq, cpu)[virq];
if (irq == -1) {
+ irq = find_unbound_irq();
+
+ set_irq_chip_and_handler_name(irq, &xen_percpu_chip,
+ handle_percpu_irq, "virq");
+
bind_virq.virq = virq;
bind_virq.vcpu = cpu;
if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
@@ -442,11 +447,6 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
BUG();
evtchn = bind_virq.port;
- irq = find_unbound_irq();
-
- set_irq_chip_and_handler_name(irq, &xen_percpu_chip,
- handle_percpu_irq, "virq");
-
evtchn_to_irq[evtchn] = irq;
irq_info[irq] = mk_virq_info(evtchn, virq);
@@ -578,41 +578,75 @@ irqreturn_t xen_debug_interrupt(int irq, void *dev_id)
{
struct shared_info *sh = HYPERVISOR_shared_info;
int cpu = smp_processor_id();
+ unsigned long *cpu_evtchn = cpu_evtchn_mask(cpu);
int i;
unsigned long flags;
static DEFINE_SPINLOCK(debug_lock);
+ struct vcpu_info *v;
spin_lock_irqsave(&debug_lock, flags);
- printk("vcpu %d\n ", cpu);
+ printk("\nvcpu %d\n ", cpu);
for_each_online_cpu(i) {
- struct vcpu_info *v = per_cpu(xen_vcpu, i);
- printk("%d: masked=%d pending=%d event_sel %08lx\n ", i,
- (get_irq_regs() && i == cpu) ? xen_irqs_disabled(get_irq_regs()) : v->evtchn_upcall_mask,
- v->evtchn_upcall_pending,
- v->evtchn_pending_sel);
+ int pending;
+ v = per_cpu(xen_vcpu, i);
+ pending = (get_irq_regs() && i == cpu)
+ ? xen_irqs_disabled(get_irq_regs())
+ : v->evtchn_upcall_mask;
+ printk("%d: masked=%d pending=%d event_sel %0*lx\n ", i,
+ pending, v->evtchn_upcall_pending,
+ (int)(sizeof(v->evtchn_pending_sel)*2),
+ v->evtchn_pending_sel);
+ }
+ v = per_cpu(xen_vcpu, cpu);
+
+ printk("\npending:\n ");
+ for (i = ARRAY_SIZE(sh->evtchn_pending)-1; i >= 0; i--)
+ printk("%0*lx%s", (int)sizeof(sh->evtchn_pending[0])*2,
+ sh->evtchn_pending[i],
+ i % 8 == 0 ? "\n " : " ");
+ printk("\nglobal mask:\n ");
+ for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
+ printk("%0*lx%s",
+ (int)(sizeof(sh->evtchn_mask[0])*2),
+ sh->evtchn_mask[i],
+ i % 8 == 0 ? "\n " : " ");
+
+ printk("\nglobally unmasked:\n ");
+ for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
+ printk("%0*lx%s", (int)(sizeof(sh->evtchn_mask[0])*2),
+ sh->evtchn_pending[i] & ~sh->evtchn_mask[i],
+ i % 8 == 0 ? "\n " : " ");
+
+ printk("\nlocal cpu%d mask:\n ", cpu);
+ for (i = (NR_EVENT_CHANNELS/BITS_PER_LONG)-1; i >= 0; i--)
+ printk("%0*lx%s", (int)(sizeof(cpu_evtchn[0])*2),
+ cpu_evtchn[i],
+ i % 8 == 0 ? "\n " : " ");
+
+ printk("\nlocally unmasked:\n ");
+ for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--) {
+ unsigned long pending = sh->evtchn_pending[i]
+ & ~sh->evtchn_mask[i]
+ & cpu_evtchn[i];
+ printk("%0*lx%s", (int)(sizeof(sh->evtchn_mask[0])*2),
+ pending, i % 8 == 0 ? "\n " : " ");
}
- printk("pending:\n ");
- for(i = ARRAY_SIZE(sh->evtchn_pending)-1; i >= 0; i--)
- printk("%08lx%s", sh->evtchn_pending[i],
- i % 8 == 0 ? "\n " : " ");
- printk("\nmasks:\n ");
- for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
- printk("%08lx%s", sh->evtchn_mask[i],
- i % 8 == 0 ? "\n " : " ");
-
- printk("\nunmasked:\n ");
- for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
- printk("%08lx%s", sh->evtchn_pending[i] & ~sh->evtchn_mask[i],
- i % 8 == 0 ? "\n " : " ");
printk("\npending list:\n");
- for(i = 0; i < NR_EVENT_CHANNELS; i++) {
+ for (i = 0; i < NR_EVENT_CHANNELS; i++) {
if (sync_test_bit(i, sh->evtchn_pending)) {
- printk(" %d: event %d -> irq %d\n",
+ int word_idx = i / BITS_PER_LONG;
+ printk(" %d: event %d -> irq %d%s%s%s\n",
cpu_from_evtchn(i), i,
- evtchn_to_irq[i]);
+ evtchn_to_irq[i],
+ sync_test_bit(word_idx, &v->evtchn_pending_sel)
+ ? "" : " l2-clear",
+ !sync_test_bit(i, sh->evtchn_mask)
+ ? "" : " globally-masked",
+ sync_test_bit(i, cpu_evtchn)
+ ? "" : " locally-masked");
}
}
@@ -663,6 +697,9 @@ static void __xen_evtchn_do_upcall(void)
int irq = evtchn_to_irq[port];
struct irq_desc *desc;
+ mask_evtchn(port);
+ clear_evtchn(port);
+
if (irq != -1) {
desc = irq_to_desc(irq);
if (desc)
@@ -800,10 +837,10 @@ static void ack_dynirq(unsigned int irq)
{
int evtchn = evtchn_from_irq(irq);
- move_native_irq(irq);
+ move_masked_irq(irq);
if (VALID_EVTCHN(evtchn))
- clear_evtchn(evtchn);
+ unmask_evtchn(evtchn);
}
static int retrigger_dynirq(unsigned int irq)
@@ -959,7 +996,7 @@ static struct irq_chip xen_dynamic_chip __read_mostly = {
.mask = disable_dynirq,
.unmask = enable_dynirq,
- .ack = ack_dynirq,
+ .eoi = ack_dynirq,
.set_affinity = set_affinity_irq,
.retrigger = retrigger_dynirq,
};
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index d409495876f1..132939f36020 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -64,9 +64,11 @@
int xen_store_evtchn;
-EXPORT_SYMBOL(xen_store_evtchn);
+EXPORT_SYMBOL_GPL(xen_store_evtchn);
struct xenstore_domain_interface *xen_store_interface;
+EXPORT_SYMBOL_GPL(xen_store_interface);
+
static unsigned long xen_store_mfn;
static BLOCKING_NOTIFIER_HEAD(xenstore_chain);
diff --git a/drivers/xen/xenfs/Makefile b/drivers/xen/xenfs/Makefile
index 25275c3bbdff..4fde9440fe1f 100644
--- a/drivers/xen/xenfs/Makefile
+++ b/drivers/xen/xenfs/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_XENFS) += xenfs.o
-xenfs-objs = super.o xenbus.o \ No newline at end of file
+xenfs-y = super.o xenbus.o privcmd.o
+xenfs-$(CONFIG_XEN_DOM0) += xenstored.o
diff --git a/drivers/xen/xenfs/privcmd.c b/drivers/xen/xenfs/privcmd.c
new file mode 100644
index 000000000000..f80be7f6eb95
--- /dev/null
+++ b/drivers/xen/xenfs/privcmd.c
@@ -0,0 +1,404 @@
+/******************************************************************************
+ * privcmd.c
+ *
+ * Interface to privileged domain-0 commands.
+ *
+ * Copyright (c) 2002-2004, K A Fraser, B Dragovic
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/uaccess.h>
+#include <linux/swap.h>
+#include <linux/smp_lock.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/tlb.h>
+#include <asm/xen/hypervisor.h>
+#include <asm/xen/hypercall.h>
+
+#include <xen/xen.h>
+#include <xen/privcmd.h>
+#include <xen/interface/xen.h>
+#include <xen/features.h>
+#include <xen/page.h>
+#include <xen/xen-ops.h>
+
+#ifndef HAVE_ARCH_PRIVCMD_MMAP
+static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma);
+#endif
+
+static long privcmd_ioctl_hypercall(void __user *udata)
+{
+ struct privcmd_hypercall hypercall;
+ long ret;
+
+ if (copy_from_user(&hypercall, udata, sizeof(hypercall)))
+ return -EFAULT;
+
+ ret = privcmd_call(hypercall.op,
+ hypercall.arg[0], hypercall.arg[1],
+ hypercall.arg[2], hypercall.arg[3],
+ hypercall.arg[4]);
+
+ return ret;
+}
+
+static void free_page_list(struct list_head *pages)
+{
+ struct page *p, *n;
+
+ list_for_each_entry_safe(p, n, pages, lru)
+ __free_page(p);
+
+ INIT_LIST_HEAD(pages);
+}
+
+/*
+ * Given an array of items in userspace, return a list of pages
+ * containing the data. If copying fails, either because of memory
+ * allocation failure or a problem reading user memory, return an
+ * error code; its up to the caller to dispose of any partial list.
+ */
+static int gather_array(struct list_head *pagelist,
+ unsigned nelem, size_t size,
+ void __user *data)
+{
+ unsigned pageidx;
+ void *pagedata;
+ int ret;
+
+ if (size > PAGE_SIZE)
+ return 0;
+
+ pageidx = PAGE_SIZE;
+ pagedata = NULL; /* quiet, gcc */
+ while (nelem--) {
+ if (pageidx > PAGE_SIZE-size) {
+ struct page *page = alloc_page(GFP_KERNEL);
+
+ ret = -ENOMEM;
+ if (page == NULL)
+ goto fail;
+
+ pagedata = page_address(page);
+
+ list_add_tail(&page->lru, pagelist);
+ pageidx = 0;
+ }
+
+ ret = -EFAULT;
+ if (copy_from_user(pagedata + pageidx, data, size))
+ goto fail;
+
+ data += size;
+ pageidx += size;
+ }
+
+ ret = 0;
+
+fail:
+ return ret;
+}
+
+/*
+ * Call function "fn" on each element of the array fragmented
+ * over a list of pages.
+ */
+static int traverse_pages(unsigned nelem, size_t size,
+ struct list_head *pos,
+ int (*fn)(void *data, void *state),
+ void *state)
+{
+ void *pagedata;
+ unsigned pageidx;
+ int ret = 0;
+
+ BUG_ON(size > PAGE_SIZE);
+
+ pageidx = PAGE_SIZE;
+ pagedata = NULL; /* hush, gcc */
+
+ while (nelem--) {
+ if (pageidx > PAGE_SIZE-size) {
+ struct page *page;
+ pos = pos->next;
+ page = list_entry(pos, struct page, lru);
+ pagedata = page_address(page);
+ pageidx = 0;
+ }
+
+ ret = (*fn)(pagedata + pageidx, state);
+ if (ret)
+ break;
+ pageidx += size;
+ }
+
+ return ret;
+}
+
+struct mmap_mfn_state {
+ unsigned long va;
+ struct vm_area_struct *vma;
+ domid_t domain;
+};
+
+static int mmap_mfn_range(void *data, void *state)
+{
+ struct privcmd_mmap_entry *msg = data;
+ struct mmap_mfn_state *st = state;
+ struct vm_area_struct *vma = st->vma;
+ int rc;
+
+ /* Do not allow range to wrap the address space. */
+ if ((msg->npages > (LONG_MAX >> PAGE_SHIFT)) ||
+ ((unsigned long)(msg->npages << PAGE_SHIFT) >= -st->va))
+ return -EINVAL;
+
+ /* Range chunks must be contiguous in va space. */
+ if ((msg->va != st->va) ||
+ ((msg->va+(msg->npages<<PAGE_SHIFT)) > vma->vm_end))
+ return -EINVAL;
+
+ rc = xen_remap_domain_mfn_range(vma,
+ msg->va & PAGE_MASK,
+ msg->mfn, msg->npages,
+ vma->vm_page_prot,
+ st->domain);
+ if (rc < 0)
+ return rc;
+
+ st->va += msg->npages << PAGE_SHIFT;
+
+ return 0;
+}
+
+static long privcmd_ioctl_mmap(void __user *udata)
+{
+ struct privcmd_mmap mmapcmd;
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ int rc;
+ LIST_HEAD(pagelist);
+ struct mmap_mfn_state state;
+
+ if (!xen_initial_domain())
+ return -EPERM;
+
+ if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd)))
+ return -EFAULT;
+
+ rc = gather_array(&pagelist,
+ mmapcmd.num, sizeof(struct privcmd_mmap_entry),
+ mmapcmd.entry);
+
+ if (rc || list_empty(&pagelist))
+ goto out;
+
+ down_write(&mm->mmap_sem);
+
+ {
+ struct page *page = list_first_entry(&pagelist,
+ struct page, lru);
+ struct privcmd_mmap_entry *msg = page_address(page);
+
+ vma = find_vma(mm, msg->va);
+ rc = -EINVAL;
+
+ if (!vma || (msg->va != vma->vm_start) ||
+ !privcmd_enforce_singleshot_mapping(vma))
+ goto out_up;
+ }
+
+ state.va = vma->vm_start;
+ state.vma = vma;
+ state.domain = mmapcmd.dom;
+
+ rc = traverse_pages(mmapcmd.num, sizeof(struct privcmd_mmap_entry),
+ &pagelist,
+ mmap_mfn_range, &state);
+
+
+out_up:
+ up_write(&mm->mmap_sem);
+
+out:
+ free_page_list(&pagelist);
+
+ return rc;
+}
+
+struct mmap_batch_state {
+ domid_t domain;
+ unsigned long va;
+ struct vm_area_struct *vma;
+ int err;
+
+ xen_pfn_t __user *user;
+};
+
+static int mmap_batch_fn(void *data, void *state)
+{
+ xen_pfn_t *mfnp = data;
+ struct mmap_batch_state *st = state;
+
+ if (xen_remap_domain_mfn_range(st->vma, st->va & PAGE_MASK, *mfnp, 1,
+ st->vma->vm_page_prot, st->domain) < 0) {
+ *mfnp |= 0xf0000000U;
+ st->err++;
+ }
+ st->va += PAGE_SIZE;
+
+ return 0;
+}
+
+static int mmap_return_errors(void *data, void *state)
+{
+ xen_pfn_t *mfnp = data;
+ struct mmap_batch_state *st = state;
+
+ put_user(*mfnp, st->user++);
+
+ return 0;
+}
+
+static struct vm_operations_struct privcmd_vm_ops;
+
+static long privcmd_ioctl_mmap_batch(void __user *udata)
+{
+ int ret;
+ struct privcmd_mmapbatch m;
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long nr_pages;
+ LIST_HEAD(pagelist);
+ struct mmap_batch_state state;
+
+ if (!xen_initial_domain())
+ return -EPERM;
+
+ if (copy_from_user(&m, udata, sizeof(m)))
+ return -EFAULT;
+
+ nr_pages = m.num;
+ if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT)))
+ return -EINVAL;
+
+ ret = gather_array(&pagelist, m.num, sizeof(xen_pfn_t),
+ m.arr);
+
+ if (ret || list_empty(&pagelist))
+ goto out;
+
+ down_write(&mm->mmap_sem);
+
+ vma = find_vma(mm, m.addr);
+ ret = -EINVAL;
+ if (!vma ||
+ vma->vm_ops != &privcmd_vm_ops ||
+ (m.addr != vma->vm_start) ||
+ ((m.addr + (nr_pages << PAGE_SHIFT)) != vma->vm_end) ||
+ !privcmd_enforce_singleshot_mapping(vma)) {
+ up_write(&mm->mmap_sem);
+ goto out;
+ }
+
+ state.domain = m.dom;
+ state.vma = vma;
+ state.va = m.addr;
+ state.err = 0;
+
+ ret = traverse_pages(m.num, sizeof(xen_pfn_t),
+ &pagelist, mmap_batch_fn, &state);
+
+ up_write(&mm->mmap_sem);
+
+ if (state.err > 0) {
+ ret = 0;
+
+ state.user = m.arr;
+ traverse_pages(m.num, sizeof(xen_pfn_t),
+ &pagelist,
+ mmap_return_errors, &state);
+ }
+
+out:
+ free_page_list(&pagelist);
+
+ return ret;
+}
+
+static long privcmd_ioctl(struct file *file,
+ unsigned int cmd, unsigned long data)
+{
+ int ret = -ENOSYS;
+ void __user *udata = (void __user *) data;
+
+ switch (cmd) {
+ case IOCTL_PRIVCMD_HYPERCALL:
+ ret = privcmd_ioctl_hypercall(udata);
+ break;
+
+ case IOCTL_PRIVCMD_MMAP:
+ ret = privcmd_ioctl_mmap(udata);
+ break;
+
+ case IOCTL_PRIVCMD_MMAPBATCH:
+ ret = privcmd_ioctl_mmap_batch(udata);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#ifndef HAVE_ARCH_PRIVCMD_MMAP
+static int privcmd_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
+ vma, vma->vm_start, vma->vm_end,
+ vmf->pgoff, vmf->virtual_address);
+
+ return VM_FAULT_SIGBUS;
+}
+
+static struct vm_operations_struct privcmd_vm_ops = {
+ .fault = privcmd_fault
+};
+
+static int privcmd_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ /* Unsupported for auto-translate guests. */
+ if (xen_feature(XENFEAT_auto_translated_physmap))
+ return -ENOSYS;
+
+ /* DONTCOPY is essential for Xen as copy_page_range is broken. */
+ vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
+ vma->vm_ops = &privcmd_vm_ops;
+ vma->vm_private_data = NULL;
+
+ return 0;
+}
+
+static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma)
+{
+ return (xchg(&vma->vm_private_data, (void *)1) == NULL);
+}
+#endif
+
+const struct file_operations privcmd_file_ops = {
+ .unlocked_ioctl = privcmd_ioctl,
+ .mmap = privcmd_mmap,
+};
diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c
index bd96340063c1..d6662b789b6b 100644
--- a/drivers/xen/xenfs/super.c
+++ b/drivers/xen/xenfs/super.c
@@ -12,6 +12,8 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/magic.h>
+#include <linux/mm.h>
+#include <linux/backing-dev.h>
#include <xen/xen.h>
@@ -22,6 +24,62 @@
MODULE_DESCRIPTION("Xen filesystem");
MODULE_LICENSE("GPL");
+static int xenfs_set_page_dirty(struct page *page)
+{
+ return !TestSetPageDirty(page);
+}
+
+static const struct address_space_operations xenfs_aops = {
+ .set_page_dirty = xenfs_set_page_dirty,
+};
+
+static struct backing_dev_info xenfs_backing_dev_info = {
+ .ra_pages = 0, /* No readahead */
+ .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
+};
+
+static struct inode *xenfs_make_inode(struct super_block *sb, int mode)
+{
+ struct inode *ret = new_inode(sb);
+
+ if (ret) {
+ ret->i_mode = mode;
+ ret->i_mapping->a_ops = &xenfs_aops;
+ ret->i_mapping->backing_dev_info = &xenfs_backing_dev_info;
+ ret->i_uid = ret->i_gid = 0;
+ ret->i_blocks = 0;
+ ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
+ }
+ return ret;
+}
+
+static struct dentry *xenfs_create_file(struct super_block *sb,
+ struct dentry *parent,
+ const char *name,
+ const struct file_operations *fops,
+ void *data,
+ int mode)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ dentry = d_alloc_name(parent, name);
+ if (!dentry)
+ return NULL;
+
+ inode = xenfs_make_inode(sb, S_IFREG | mode);
+ if (!inode) {
+ dput(dentry);
+ return NULL;
+ }
+
+ inode->i_fop = fops;
+ inode->i_private = data;
+
+ d_add(dentry, inode);
+ return dentry;
+}
+
static ssize_t capabilities_read(struct file *file, char __user *buf,
size_t size, loff_t *off)
{
@@ -44,10 +102,23 @@ static int xenfs_fill_super(struct super_block *sb, void *data, int silent)
[1] = {},
{ "xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR },
{ "capabilities", &capabilities_file_ops, S_IRUGO },
+ { "privcmd", &privcmd_file_ops, S_IRUSR|S_IWUSR },
{""},
};
+ int rc;
- return simple_fill_super(sb, XENFS_SUPER_MAGIC, xenfs_files);
+ rc = simple_fill_super(sb, XENFS_SUPER_MAGIC, xenfs_files);
+ if (rc < 0)
+ return rc;
+
+ if (xen_initial_domain()) {
+ xenfs_create_file(sb, sb->s_root, "xsd_kva",
+ &xsd_kva_file_ops, NULL, S_IRUSR|S_IWUSR);
+ xenfs_create_file(sb, sb->s_root, "xsd_port",
+ &xsd_port_file_ops, NULL, S_IRUSR|S_IWUSR);
+ }
+
+ return rc;
}
static int xenfs_get_sb(struct file_system_type *fs_type,
@@ -66,11 +137,25 @@ static struct file_system_type xenfs_type = {
static int __init xenfs_init(void)
{
- if (xen_domain())
- return register_filesystem(&xenfs_type);
+ int err;
+ if (!xen_domain()) {
+ printk(KERN_INFO "xenfs: not registering filesystem on non-xen platform\n");
+ return 0;
+ }
+
+ err = register_filesystem(&xenfs_type);
+ if (err) {
+ printk(KERN_ERR "xenfs: Unable to register filesystem!\n");
+ goto out;
+ }
+
+ err = bdi_init(&xenfs_backing_dev_info);
+ if (err)
+ unregister_filesystem(&xenfs_type);
+
+ out:
- printk(KERN_INFO "XENFS: not registering filesystem on non-xen platform\n");
- return 0;
+ return err;
}
static void __exit xenfs_exit(void)
diff --git a/drivers/xen/xenfs/xenfs.h b/drivers/xen/xenfs/xenfs.h
index 51f08b2d0bf1..b68aa6200003 100644
--- a/drivers/xen/xenfs/xenfs.h
+++ b/drivers/xen/xenfs/xenfs.h
@@ -2,5 +2,8 @@
#define _XENFS_XENBUS_H
extern const struct file_operations xenbus_file_ops;
+extern const struct file_operations privcmd_file_ops;
+extern const struct file_operations xsd_kva_file_ops;
+extern const struct file_operations xsd_port_file_ops;
#endif /* _XENFS_XENBUS_H */
diff --git a/drivers/xen/xenfs/xenstored.c b/drivers/xen/xenfs/xenstored.c
new file mode 100644
index 000000000000..fef20dbc6a5c
--- /dev/null
+++ b/drivers/xen/xenfs/xenstored.c
@@ -0,0 +1,68 @@
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+
+#include <xen/page.h>
+
+#include "xenfs.h"
+#include "../xenbus/xenbus_comms.h"
+
+static ssize_t xsd_read(struct file *file, char __user *buf,
+ size_t size, loff_t *off)
+{
+ const char *str = (const char *)file->private_data;
+ return simple_read_from_buffer(buf, size, off, str, strlen(str));
+}
+
+static int xsd_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static int xsd_kva_open(struct inode *inode, struct file *file)
+{
+ file->private_data = (void *)kasprintf(GFP_KERNEL, "0x%p",
+ xen_store_interface);
+ if (!file->private_data)
+ return -ENOMEM;
+ return 0;
+}
+
+static int xsd_kva_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ size_t size = vma->vm_end - vma->vm_start;
+
+ if ((size > PAGE_SIZE) || (vma->vm_pgoff != 0))
+ return -EINVAL;
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ virt_to_pfn(xen_store_interface),
+ size, vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+const struct file_operations xsd_kva_file_ops = {
+ .open = xsd_kva_open,
+ .mmap = xsd_kva_mmap,
+ .read = xsd_read,
+ .release = xsd_release,
+};
+
+static int xsd_port_open(struct inode *inode, struct file *file)
+{
+ file->private_data = (void *)kasprintf(GFP_KERNEL, "%d",
+ xen_store_evtchn);
+ if (!file->private_data)
+ return -ENOMEM;
+ return 0;
+}
+
+const struct file_operations xsd_port_file_ops = {
+ .open = xsd_port_open,
+ .read = xsd_read,
+ .release = xsd_release,
+};